class OuterClass {
...
static class StaticNestedClass {
...
}
class InnerClass {
...
}
}
מחלקות פנימיות אלו נקראות מקוננות. הם מחולקים ל-2 סוגים:
- מחלקות מקוננות לא סטטיות. אלה נקראים גם כיתות פנימיות.
- מחלקות מקוננות סטטיות.
- כיתה מקומית
- כיתה אנונימית

public class Bicycle {
private String model;
private int weight;
public Bicycle(String model, int weight) {
this.model = model;
this.weight = weight;
}
public void start() {
System.out.println("Let's go!");
}
public class Handlebar {
public void right() {
System.out.println("Steer right!");
}
public void left() {
System.out.println("Steer left!");
}
}
public class Seat {
public void up() {
System.out.println("Seat up!");
}
public void down() {
System.out.println("Seat down!");
}
}
}
הנה לנו הכיתה Bicycle
. יש לו 2 שדות ושיטה אחת: start()
. 
Handlebar
ו Seat
. הקוד שלהם כתוב בתוך Bicycle
הכיתה. אלה הם שיעורים מן המניין: כפי שאתה יכול לראות, לכל אחד מהם יש שיטות משלו. בשלב זה, אולי יש לך שאלה: למה בכלל שנכניס מחלקה אחת לכיתה אחרת? למה לעשות להם מעמדות פנימיים? ובכן, נניח שאנחנו צריכים שיעורים נפרדים למושגים של כידון ומושב בתוכנית שלנו. כמובן, זה לא הכרחי עבורנו להפוך אותם לקנן! אנחנו יכולים לעשות שיעורים רגילים. לדוגמה, כך:
public class Handlebar {
public void right() {
System.out.println("Steer right!");
}
public void left() {
System.out.println("Steer left");
}
}
public class Seat {
public void up() {
System.out.println("Seat up!");
}
public void down() {
System.out.println("Seat down!");
}
}
שאלה טובה מאוד! כמובן, אנחנו לא מוגבלים על ידי הטכנולוגיה. לעשות את זה זו בהחלט אופציה. כאן, הדבר החשוב הוא יותר עיצוב נכון של השיעורים מנקודת מבט של תוכנית ספציפית ומטרתה. מחלקות פנימיות מיועדות להפרדת ישות המחוברת בל יינתק לישות אחרת. כידון, מושבים ודוושות הם מרכיבים של אופניים. כשהם מופרדים מהאופניים, הם לא הגיוניים במיוחד. אם היינו הופכים את כל המושגים האלה לשיעורים ציבוריים נפרדים, היה לנו את הקוד כזה בתוכנית שלנו:
public class Main {
public static void main(String[] args) {
Handlebar handlebar = new Handlebar();
handlebar.right();
}
}
הממ... את המשמעות של הקוד הזה אפילו קשה להסביר. יש לנו איזה כידון מעורפל (למה זה נחוץ? אין מושג, למען האמת). והידית הזו מסתובבת ימינה... לגמרי לבד, בלי אופניים... משום מה. על ידי הפרדת קונספט הכידון לקונספט האופניים, איבדנו קצת הגיון בתוכנית שלנו. באמצעות מחלקה פנימית, הקוד נראה שונה מאוד:
public class Main {
public static void main(String[] args) {
Bicycle peugeot = new Bicycle("Peugeot", 120);
Bicycle.Handlebar handlebar = peugeot.new Handlebar();
Bicycle.Seat seat = peugeot.new Seat();
seat.up();
peugeot.start();
handlebar.left();
handlebar.right();
}
}
פלט מסוף:
Seat up!
Let's go!
Steer left!
Steer right!
עכשיו מה שאנחנו רואים פתאום הגיוני! :) יצרנו חפץ לאופניים. יצרנו שני "תתי חפצים" לאופניים - כידון ומושב. הרמנו את המושב לנוחות ויצאנו לדרך: דיווש והיגוי לפי הצורך! :) השיטות שאנו צריכים נקראות על האובייקטים המתאימים. הכל פשוט ונוח. בדוגמה זו, הפרדת הכידון והמושב משפרת את המעטפת (אנחנו מסתירים נתונים על חלקי האופניים בתוך המחלקה הרלוונטית) ומאפשרת לנו ליצור הפשטה מפורטת יותר. עכשיו בואו נסתכל על מצב אחר. נניח שאנו רוצים ליצור תוכנית המדמה חנות אופניים וחלקי חילוף לאופניים. 
-
אובייקט של מחלקה פנימית לא יכול להתקיים ללא אובייקט של מחלקה חיצונית.
זה הגיוני: זו הסיבה שעשינו את השיעורים
Seat
והפנימיותHandlebar
בתוכנית שלנו - כדי שלא נצא עם כידון ומושבים יתומים.קוד זה אינו קומפילציה:
public static void main(String[] args) { Handlebar handlebar = new Handlebar(); }
תכונה חשובה נוספת נובעת מכך:
-
לאובייקט של מחלקה פנימית יש גישה למשתנים של המחלקה החיצונית.
לדוגמה, בואו נוסיף
int seatPostDiameter
משתנה (המייצג את קוטר עמוד המושב) לכיתה שלנוBicycle
.לאחר מכן, במחלקה
Seat
הפנימית, נוכל ליצורdisplaySeatProperties()
שיטה המציגה את מאפייני המושב:public class Bicycle { private String model; private int weight; private int seatPostDiameter; public Bicycle(String model, int weight, int seatPostDiameter) { this.model = model; this.weight = weight; this.seatPostDiameter = seatPostDiameter; } public void start() { System.out.println("Let's go!"); } public class Seat { public void up() { System.out.println("Seat up!"); } public void down() { System.out.println("Seat down!"); } public void displaySeatProperties() { System.out.println("Seat properties: seatpost diameter = " + Bicycle.this.seatPostDiameter); } } }
ועכשיו אנחנו יכולים להציג את המידע הזה בתוכנית שלנו:
public class Main { public static void main(String[] args) { Bicycle bicycle = new Bicycle("Peugeot", 120, 40); Bicycle.Seat seat = bicycle.new Seat(); seat.displaySeatProperties(); } }
פלט מסוף:
Seat properties: seatpost diameter = 40
הערה:המשתנה החדש מוכרז עם שינוי הגישה המחמיר ביותר (
private
). ועדיין למעמד הפנימי יש גישה! -
לא ניתן ליצור אובייקט של מחלקה פנימית בשיטה סטטית של מחלקה חיצונית.
זה מוסבר על ידי המאפיינים הספציפיים של אופן ארגון השיעורים הפנימיים. למחלקה פנימית יכולים להיות בנאים עם פרמטרים, או רק בנאי ברירת המחדל. אבל בלי קשר, כאשר אנו יוצרים אובייקט של מחלקה פנימית, הפניה לאובייקט של המחלקה החיצונית מועברת באופן בלתי נראה לאובייקט שנוצר של המחלקה הפנימית. אחרי הכל, נוכחות של התייחסות אובייקט כזו היא דרישה מוחלטת. אחרת, לא נוכל ליצור אובייקטים של המעמד הפנימי.
אבל אם שיטה של המחלקה החיצונית היא סטטית, אז אולי אין לנו אובייקט של המחלקה החיצונית! וזו תהיה הפרה של ההיגיון של איך מעמד פנימי עובד. במצב זה, המהדר יפיק שגיאה:
public static Seat createSeat() { // Bicycle.this cannot be referenced from a static context return new Seat(); }
-
מחלקה פנימית אינה יכולה להכיל משתנים ושיטות סטטיות.
ההיגיון זהה: שיטות ומשתנים סטטיים יכולים להתקיים ולהיקרא או להתייחס אליהם גם בהיעדר אובייקט.
אבל ללא אובייקט של המעמד החיצוני, לא תהיה לנו גישה למעמד הפנימי.
סתירה ברורה! זו הסיבה שמשתנים ושיטות סטטיות אינם מותרים במחלקות פנימיות.
המהדר יפיק שגיאה אם תנסה ליצור אותם:
public class Bicycle { private int weight; public class Seat { // An inner class cannot have static declarations public static void displaySeatProperties() { System.out.println("Seat properties: seatpost diameter = " + Bicycle.this.seatPostDiameter); } } }
-
בעת יצירת אובייקט של מחלקה פנימית, משנה הגישה שלו חשוב.
ניתן לסמן מחלקה פנימית במתקני הגישה הסטנדרטיים:
public
,private
,protected
, וpackage private
.למה זה משנה?
זה משפיע על המקום שבו אנחנו יכולים ליצור מופעים של המעמד הפנימי בתוכנית שלנו.
אם
Seat
המחלקה שלנו מוצהרת כ-public
, נוכל ליצורSeat
אובייקטים בכל מחלקה אחרת. הדרישה היחידה היא שאובייקט של המחלקה החיצונית חייב להתקיים גם כן.אגב, כבר עשינו את זה כאן:
public class Main { public static void main(String[] args) { Bicycle peugeot = new Bicycle("Peugeot", 120); Bicycle.Handlebar handlebar = peugeot.new Handlebar(); Bicycle.Seat seat = peugeot.new Seat(); seat.up(); peugeot.start(); handlebar.left(); handlebar.right(); } }
הגענו בקלות לכיתה
Handlebar
הפנימית מהכיתהMain
.אם נכריז על המחלקה הפנימית כ-
private
, נוכל ליצור אובייקטים רק בתוך המחלקה החיצונית.אנחנו כבר לא יכולים ליצור
Seat
אובייקט "בחוץ":private class Seat { // Methods } public class Main { public static void main(String[] args) { Bicycle bicycle = new Bicycle("Peugeot", 120, 40); // Bicycle.Seat has private access in Bicycle Bicycle.Seat seat = bicycle.new Seat(); } }
בטח כבר הבנתם את ההיגיון :)
-
משנה גישה למחלקות פנימיות פועלות כמו עבור משתנים רגילים.
השינוי
protected
מספק גישה למשתנה מופע בתתי מחלקות ומחלקות שנמצאות באותה חבילה.protected
עובד גם עבור כיתות פנימיות. אנו יכולים ליצורprotected
אובייקטים של המעמד הפנימי:- במעמד החיצוני;
- בתתי המחלקות שלו;
- בשיעורים שנמצאים באותה חבילה.
אם למחלקה הפנימית אין משנה גישה (
package private
), ניתן ליצור אובייקטים של המחלקה הפנימית:- במעמד החיצוני;
- בשיעורים שנמצאים באותה חבילה.
אתה מכיר את השינויים כבר זמן רב, אז אין כאן בעיות.
GO TO FULL VERSION