המחלקה הפנימית של Java מוגדרת בתוך גוף של מחלקה אחרת. המחלקה הפנימית של Java יכולה להיות נקבעת כפרטית, ציבורית, מוגנת, או עם גישה ברירת מחדל בעוד שמחלקה חיצונית יכולה להיות רק עם גישה ציבורית או ברירת מחדל. מחלקות מקוננות ב-Java נחלקות לשני סוגים.
-
מחלקה מקוננת סטטית
אם המחלקה המקוננת היא סטטית, אז היא נקראת מחלקה מקוננת סטטית. מחלקות מקוננות סטטיות יכולות לגשת רק לחברים סטטיים של המחלקה החיצונית. מחלקה מקוננת סטטית היא זהה לכל מחלקה אחרת ברמה עליונה ומקוננת רק למטרות עטיפה. ניתן ליצור אובייקט של מחלקה סטטית עם ההצהרה הבאה.
OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();
-
מחלקה פנימית של Java
כל מחלקה מקוננת לא סטטית ידועה כמחלקה פנימית ב-Java. מחלקה פנימית ב-Java מתוארת עם אובייקט של המחלקה והיא יכולה לגשת לכל המשתנים והשיטות של המחלקה החיצונית. מאחר ומחלקות פנימיות מקושרות לאינסטנס, אין לנו אפשרות להכיל בהם משתנים סטטיים. אובייקטי המחלקה הפנימית ב-Java הם חלק מאובייקט המחלקה החיצונית וכדי ליצור מופע של המחלקה הפנימית, עלינו תחילה ליצור מופע של המחלקה החיצונית. מחלקה פנימית ב-Java ניתן ליצור כך;
OuterClass outerObject = new OuterClass(); OuterClass.InnerClass innerObject = outerObject.new InnerClass();
קיימות שתי סוגיות מיוחדות של מחלקות פנימיות ב-Java.
-
מחלקה פנימית מקומית
אם מחלקה מוגדרת בתוך גוף של שיטה, היא מכונה מחלקה פנימית מקומית. מכיוון שהמחלקה הפנימית המקומית אינה מקושרת לאובייקט, לא ניתן להשתמש באפשרויות גישה כגון private, public או protected עליה. המודיפיקטורים המותרים הינם abstract או final בלבד. מחלקה פנימית מקומית יכולה לגשת לכל החברים של המחלקה הסוגרת ולמשתנים סופיים מקומיים בטווח בו היא מוגדרת. בנוסף, היא יכולה גם לגשת למשתנה מקומי שאינו סופי של השיטה שבה היא מוגדרת, אך אין באפשרותה לשנות אותם. לכן, אם תנסו להדפיס את ערך המשתנה המקומי שאינו סופי, תאפשרו זאת, אך אם תנסו לשנות את ערכו מתוך מחלקת מחלקה פנימית מקומית, תקבלו שגיאת זמן קומפילציה. מחלקה פנימית מקומית יכולה להיגדר כך:
package com.journaldev.innerclasses; public class MainClass { private String s_main_class; public void print() { String s_print_method = ""; // מחלקה פנימית מקומית בתוך השיטה class Logger { // יכולה לגשת למשתנים של המחלקה הסובבת String name = s_main_class; // יכולה לגשת למשתנים של השיטה שאינם סופיים String name1 = s_print_method; public void foo() { String name1 = s_print_method; // קטע הקוד למטה יזרוק שגיאת זמן קומפילציה: // משתנה מקומי s_print_method שהוגדר בטווח סובב חייב להיות סופי או סופי באופן מופעל // s_print_method= ":"; } } // יצירת מופע של מחלקה פנימית מקומית בשיטה לשימוש Logger logger = new Logger(); } }
ניתן להגדיר מחלקה פנימית מקומית בתוך כל בלוק, כגון בלוק סטטי, בלוק if-else וכו'. אולם, במקרה זה, הטווח של המחלקה יהיה מאוד מוגבל.
public class MainClass { static { class Foo { } Foo f = new Foo(); } public void bar() { if(1 < 2) { class Test { } Test t1 = new Test(); } // למטה יזרוק שגיאה בגלל טווח המחלקה //Test t = new Test(); //Foo f = new Foo(); } }
-
מחלקה פנימית אנונימית
מחלקה פנימית מקומית בלעדיה שאין לה שם ידועה כמחלקה פנימית אנונימית. מחלקה אנונימית מוגדרת ומופעלת בהגדרה יחידה. מחלקה פנימית אנונימית תמיד מרחיבה מחלקה או מממשת ממשק. מאחר ולמחלקה אנונימית אין שם, אי אפשר להגדיר בנאי עבור מחלקה אנונימית. מחלקות פנימיות אנונימיות נגישות רק בנקודה בה הן מוגדרות. זה קצת קשה להגדיר איך ליצור מחלקה פנימית אנונימית, נראה את השימוש בזמן אמת בתוך התוכנית הבדיקה למטה.
כאן יש כיצד מממשים מחלקה פנימית ב-Java, מחלקה פנימית סטטית, מחלקה פנימית מקומית, ומחלקה פנימית אנונימית. OuterClass.java
package com.journaldev.nested;
import java.io.File;
import java.io.FilenameFilter;
public class OuterClass {
private static String name = "OuterClass";
private int i;
protected int j;
int k;
public int l;
//בונה של OuterClass
public OuterClass(int i, int j, int k, int l) {
this.i = i;
this.j = j;
this.k = k;
this.l = l;
}
public int getI() {
return this.i;
}
//מחלקה מקוננת סטטית, יכולה לגשת למשתנים/שיטות סטטיים של OuterClass
static class StaticNestedClass {
private int a;
protected int b;
int c;
public int d;
public int getA() {
return this.a;
}
public String getName() {
return name;
}
}
//מחלקה מקוננת, לא סטטית ויכולה לגשת לכל המשתנים/השיטות של המחלקה החיצונית
class InnerClass {
private int w;
protected int x;
int y;
public int z;
public int getW() {
return this.w;
}
public void setValues() {
this.w = i;
this.x = j;
this.y = k;
this.z = l;
}
@Override
public String toString() {
return "w=" + w + ":x=" + x + ":y=" + y + ":z=" + z;
}
public String getName() {
return name;
}
}
//מחלקה מקוננת מקומית
public void print(String initial) {
//מחלקה מקוננת מקומית inside the method
class Logger {
String name;
public Logger(String name) {
this.name = name;
}
public void log(String str) {
System.out.println(this.name + ": " + str);
}
}
Logger logger = new Logger(initial);
logger.log(name);
logger.log("" + this.i);
logger.log("" + this.j);
logger.log("" + this.k);
logger.log("" + this.l);
}
//מחלקה מקוננת אנונימית
public String[] getFilesInDir(String dir, final String ext) {
File file = new File(dir);
//מחלקה מקוננת אנונימית implementing FilenameFilter interface
String[] filesList = file.list(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith(ext);
}
});
return filesList;
}
}
הנה תוכנית הבדיקה שמציגה כיצד ליצור ולהשתמש במחלקה הפנימית ב-Java. InnerClassTest.java
package com.journaldev.nested;
import java.util.Arrays;
//מחלקות מקוננות יכולות לשמש בimport ליצירה קלה
import com.journaldev.nested.OuterClass.InnerClass;
import com.journaldev.nested.OuterClass.StaticNestedClass;
public class InnerClassTest {
public static void main(String[] args) {
OuterClass outer = new OuterClass(1,2,3,4);
//דוגמה למחלקות מקוננות סטטיות
StaticNestedClass staticNestedClass = new StaticNestedClass();
StaticNestedClass staticNestedClass1 = new StaticNestedClass();
System.out.println(staticNestedClass.getName());
staticNestedClass.d=10;
System.out.println(staticNestedClass.d);
System.out.println(staticNestedClass1.d);
//דוגמה למחלקות מקוננות
InnerClass innerClass = outer.new InnerClass();
System.out.println(innerClass.getName());
System.out.println(innerClass);
innerClass.setValues();
System.out.println(innerClass);
//קריאה לשיטה באמצעות מחלקה מקוננת מקומית
outer.print("Outer");
//קריאה לשיטה באמצעות מחלקה מקוננת אנונימית
System.out.println(Arrays.toString(outer.getFilesInDir("src/com/journaldev/nested", ".java")));
System.out.println(Arrays.toString(outer.getFilesInDir("bin/com/journaldev/nested", ".class")));
}
}
הנה הפלט של תוכנית הדוגמה למחלקה פנימית ב-Java שלמעלה.
OuterClass
10
0
OuterClass
w=0:x=0:y=0:z=0
w=1:x=2:y=3:z=4
Outer: OuterClass
Outer: 1
Outer: 2
Outer: 3
Outer: 4
[NestedClassTest.java, OuterClass.java]
[NestedClassTest.class, OuterClass$1.class, OuterClass$1Logger.class, OuterClass$InnerClass.class, OuterClass$StaticNestedClass.class, OuterClass.class]
שים לב שכאשר OuterClass מודרג, קבצי מחלקה נפרדים נוצרים עבור המחלקה הפנימית, המחלקה הפנימית המקומית והמחלקה המקוננת הסטטית.
יתרונות של מחלקה פנימית ב-Java
- אם מחלקה שימושית רק למחלקה אחת, יש משמעות לשמור עליה מקוננת וביחד. זה מסייע באריזת המחלקות.
- מחלקות פנימיות ב-Java מממשות אינקפסולציה. שימו לב שמחלקות פנימיות יכולות לגשת לחברים פרטיים של מחלקת החוץ ובו זמן אנחנו יכולים להסתיר מחלקה פנימית מהעולם החיצון.
- שמירת מחלקה קטנה בתוך מחלקות ראשיות ממקמת את הקוד קרוב יותר למקום שבו הוא משמש והופכת את הקוד לקריא ולקל לתחזוקה יותר.
זה הכל לגבי מחלקה פנימית ב-Java.
Source:
https://www.digitalocean.com/community/tutorials/java-inner-class