Στην Java, ο όρος “εμφωλευμένες κλάσεις” αναφέρεται στη δυνατότητα να δημιουργήσουμε μια κλάση μέσα στο εσωτερικό μιας άλλης κλάσης. Αυτή η τεχνική ονομάζεται εμφωλευμένες κλάσεις ή εσωτερικές κλάσεις.
Οι εμφωλευμένες κλάσεις μας επιτρέπουν να οργανώσουμε τον κώδικά μας και να αυξήσουμε την ασφάλεια και τη συντήρηση του. Μια εμφωλευμένη κλάση έχει πρόσβαση σε όλα τα μέλη (μεταβλητές, μέθοδοι) της εξωτερικής κλάσης, συμπεριλαμβανομένων των ιδιωτικών μελών της.
Οι εμφωλευμένες κλάσεις μπορούν να χρησιμοποιηθούν για την οργάνωση του κώδικα, την υλοποίηση λειτουργικοτήτων που αφορούν μόνο την εξωτερική κλάση, και την επίτευξη μιας καθαρής και συμπαγούς δομής κώδικα.
Για να έχετε πρόσβαση στην εσωτερική κλάση, δημιουργήστε ένα αντικείμενο της εξωτερικής κλάσης και στη συνέχεια δημιουργήστε ένα αντικείμενο της εσωτερικής κλάσης:
// Αυτή είναι μια κλάση OuterClass class OuterClass { int x = 10; // Αυτή είναι μια εσωτερική κλάση InnerClass class InnerClass { int y = 5; } } public class Main { public static void main(String[] args) { OuterClass myOuter = new OuterClass(); // Δημιουργία αντικειμένου OuterClass OuterClass.InnerClass myInner = myOuter.new InnerClass(); // Δημιουργία αντικειμένου InnerClass System.out.println(myInner.y + myOuter.x); // Εμφάνιση της τιμής x και y } }
Ο παραπάνω κώδικας δημιουργεί δύο κλάσεις, την OuterClass
και την InnerClass
. Η OuterClass
είναι μια εξωτερική κλάση, ενώ η InnerClass
είναι μια εσωτερική κλάση που βρίσκεται μέσα στην OuterClass
.
Η OuterClass
περιέχει ένα πεδίο x
με τιμή 10. Η InnerClass
περιέχει ένα πεδίο y
με τιμή 5.
Στην Main
κλάση, στην μέθοδο main
, δημιουργείται ένα αντικείμενο myOuter
της κλάσης OuterClass
. Έπειτα, δημιουργείται ένα αντικείμενο myInner
της κλάσης InnerClass
μέσα από το myOuter
αντικείμενο. Τελικά, εκτυπώνεται η τιμή του πεδίου y
του myInner
αντικειμένου προσθέτοντας την τιμή του πεδίου x
του myOuter
αντικειμένου.
Συνολικά, ο κώδικας εμφανίζει το αποτέλεσμα της πρόσθεσης των τιμών των πεδίων x
και y
, δηλαδή 15, στην οθόνη.
Εσωτερικές κλάσεις μπορούν να χρησιμοποιηθούν και για να κάνουν τον κώδικα πιο ασφαλή ή οργανωμένο. Ένα ακόμη παράδειγμα είναι η χρήση των private
εσωτερικών κλάσεων για την προστασία ευαίσθητων δεδομένων από τις εξωτερικές κλάσεις.
Σε αντίθεση με μια “κανονική” κλάση, μια εσωτερική κλάση μπορεί να είναι ιδιωτική ή προστατευμένη. Αν δεν θέλετε αντικείμενα από το εξωτερικό να έχουν πρόσβαση στην εσωτερική κλάση, δηλώστε την κλάση ως ιδιωτική:
class OuterClass { int x = 10; // Μια μεταβλητή x στην εξωτερική κλάση OuterClass private class InnerClass { int y = 5; // Μια μεταβλητή y στην ιδιωτική εσωτερική κλάση InnerClass } } public class Main { public static void main(String[] args) { OuterClass myOuter = new OuterClass(); // Δεν είναι δυνατή η δημιουργία αντικειμένου για την ιδιωτική εσωτερική κλάση // OuterClass.InnerClass myInner = myOuter.new InnerClass(); } }
Ο παραπάνω κώδικας περιέχει δύο κλάσεις, την OuterClass
και την Main
. Ας τις αναλύσουμε μία προς μία:
OuterClass
: Αυτή η κλάση έχει ένα πεδίοx
τύπουint
με τιμή 10. Το πεδίο αυτό είναι προσβάσιμο από οποιαδήποτε μέθοδο της κλάσηςOuterClass
.Main
: Αυτή η κλάση περιέχει τη μέθοδοmain
, η οποία είναι η εκκίνηση του προγράμματος. Μέσα στηνmain
δημιουργείται ένα αντικείμενο τηςOuterClass
με το όνομαmyOuter
.
Σημειώνεται ότι υπάρχει και η ιδιωτική εσωτερική κλάση InnerClass
μέσα στην OuterClass
, αλλά η προσπέλαση της δημιουργίας αντικειμένου αυτής της κλάσης από την Main
κλάση έχει σχολιαστεί, δηλαδή έχει απενεργοποιηθεί με τη χρήση σχόλιων.
Αν προσπαθήσετε να έχετε πρόσβαση σε μια ιδιωτική εσωτερική κλάση από μια κλάση εκτός της θυγατρικής κλάσης, θα προκληθεί σφάλμα κατά τη διάρκεια του χρόνου εκτέλεσης (runtime error):
class OuterClass { int x = 10; private class InnerClass { int y = 5; } } public class Main { public static void main(String[] args) { OuterClass myOuter = new OuterClass(); // Δεν είναι δυνατή η δημιουργία αντικειμένου για την ιδιωτική εσωτερική κλάση // OuterClass.InnerClass myInner = myOuter.new InnerClass(); } } class AnotherClass { // Αυτό προκαλεί σφάλμα κατά τη διάρκεια της εκτέλεσης // OuterClass.InnerClass myInner = new OuterClass().new InnerClass(); }
Στο παραπάνω παράδειγμα, η InnerClass
είναι ιδιωτική και προσπαθούμε να δημιουργήσουμε ένα αντικείμενο της από μια άλλη κλάση. Αυτό θα προκαλέσει ένα runtime error.
Το σφάλμα που θα λάβετε στο προηγούμενο παράδειγμα είναι το εξής:
Exception in thread "main" java.lang.Error: Unresolved compilation problem: The type OuterClass.InnerClass is not visible at AnotherClass.<init>(AnotherClass.java:3) at Main.main(Main.java:14)
Η αιτία του σφάλματος είναι ότι η ιδιωτική εσωτερική κλάση InnerClass
δεν είναι ορατή από άλλες κλάσεις εκτός από την OuterClass
.
Μια εσωτερική κλάση μπορεί επίσης να είναι στατική (static), το οποίο σημαίνει ότι μπορείτε να έχετε πρόσβαση σε αυτήν χωρίς να δημιουργήσετε ένα αντικείμενο της εξωτερικής κλάσης:
class OuterClass { int x = 10; static class InnerClass { int y = 5; } } public class Main { public static void main(String[] args) { // Δεν χρειάζεται να δημιουργήσετε ένα αντικείμενο της εξωτερικής κλάσης για να έχετε πρόσβαση στη στατική εσωτερική κλάση OuterClass.InnerClass myInner = new OuterClass.InnerClass(); System.out.println(myInner.y); } }
Στο παραπάνω παράδειγμα, η εσωτερική κλάση InnerClass
είναι στατική. Αυτό σημαίνει ότι μπορείτε να έχετε πρόσβαση σε αυτήν χωρίς να χρειάζεται να δημιουργήσετε ένα αντικείμενο της εξωτερικής κλάσης OuterClass
.
Ένα από τα πλεονεκτήματα των εσωτερικών κλάσεων είναι ότι μπορούν να έχουν πρόσβαση στα χαρακτηριστικά και τις μεθόδους της εξωτερικής κλάσης:
class OuterClass { int x = 10; // Μια μεταβλητή τύπου int με την τιμή 10 class InnerClass { public int myInnerMethod() { return x; // Επιστρέφει την τιμή της μεταβλητής x από την εξωτερική κλάση } } } public class Main { public static void main(String[] args) { OuterClass myOuter = new OuterClass(); // Δημιουργία ενός αντικειμένου της εξωτερικής κλάσης OuterClass.InnerClass myInner = myOuter.new InnerClass(); // Δημιουργία ενός αντικειμένου της εσωτερικής κλάσης System.out.println(myInner.myInnerMethod()); // Εκτύπωση της τιμής που επιστρέφει η μέθοδος myInnerMethod() } }
Στο παραπάνω παράδειγμα, η εσωτερική κλάση InnerClass
έχει πρόσβαση στη μεταβλητή x
της εξωτερικής κλάσης OuterClass
. Μέσω της δημιουργίας ενός αντικειμένου της OuterClass
και του ακόλουθου δημιουργίας ενός αντικειμένου της InnerClass
, μπορούμε να καλέσουμε τη μέθοδο myInnerMethod()
και να επιστρέψουμε την τιμή της μεταβλητής x
.
Το παραπάνω παράδειγμα θα εμφανίσει το ακόλουθο αποτέλεσμα:
10
Στη μέθοδο main()
, δημιουργούμε ένα αντικείμενο της OuterClass
και ένα αντικείμενο της InnerClass
. Στη συνέχεια, καλούμε τη μέθοδο myInnerMethod()
στο αντικείμενο της InnerClass
, η οποία επιστρέφει την τιμή της μεταβλητής x
της εξωτερικής κλάσης. Τέλος, εκτυπώνουμε την τιμή που επιστρέφει η μέθοδος myInnerMethod()
, η οποία είναι 10
.