Classes statiques et classes internes en Java

Les classes imbriquées sont des classes déclarées comme membres d'autres classes ou étendues. L'imbrication des classes est un moyen de mieux organiser votre code. Par exemple, supposons que vous ayez une classe non imbriquée (également appelée classe de niveau supérieur ) qui stocke les objets dans un tableau redimensionnable, suivie d'une classe d'itérateur qui renvoie chaque objet. Plutôt que de polluer l'espace de noms de la classe de niveau supérieur, vous pouvez déclarer la classe iterator en tant que membre de la classe de collection de tableaux redimensionnables. Cela fonctionne parce que les deux sont étroitement liés.

En Java, les classes imbriquées sont classées en tant que classes membres statiques ou classes internes . Les classes internes sont des classes membres non statiques, des classes locales ou des classes anonymes. Dans ce didacticiel, vous apprendrez à utiliser des classes membres statiques et les trois types de classes internes dans votre code Java.

Évitez les fuites de mémoire dans les classes imbriquées

Consultez également le conseil Java associé à ce didacticiel, où vous découvrirez pourquoi les classes imbriquées sont vulnérables aux fuites de mémoire.

Classes statiques en Java

Dans mon didacticiel Java 101 Classes and objects in Java, vous avez appris à déclarer des champs statiques et des méthodes statiques en tant que membres d'une classe. Dans l'initialisation de classe et d'objet en Java, vous avez appris à déclarer des initialiseurs statiques en tant que membres d'une classe. Vous allez maintenant apprendre à déclarer des classes statiques . Formellement appelées classes membres statiques , il s'agit de classes imbriquées que vous déclarez au même niveau que ces autres entités statiques, à l'aide du staticmot - clé. Voici un exemple de déclaration de classe de membre statique:

 class C { static int f; static void m() {} static { f = 2; } static class D { // members } } 

Cet exemple présente une classe de niveau supérieur Cavec un champ statique f, une méthode statique m(), un initialiseur statique et une classe de membre statique D. Notez que Dc'est un membre de C. Le champ statique f, la méthode statique m()et l'initialiseur statique sont également membres de C. Étant donné que tous ces éléments appartiennent à la classe C, elle est connue sous le nom de classe englobante . La classe Dest connue sous le nom de classe incluse .

Règles de clôture et d'accès

Bien qu'elle soit incluse, une classe membre statique ne peut pas accéder aux champs d'instance de la classe englobante et appeler ses méthodes d'instance. Cependant, il peut accéder aux champs statiques de la classe englobante et invoquer ses méthodes statiques, même les membres déclarés private. Pour le démontrer, le listing 1 déclare un EnclosingClassavec un SMClass.

Listing 1. Déclarer une classe membre statique (EnclosingClass.java, version 1)

 class EnclosingClass { private static String s; private static void m1() { System.out.println(s); } static void m2() { SMClass.accessEnclosingClass(); } static class SMClass { static void accessEnclosingClass() { s = "Called from SMClass's accessEnclosingClass() method"; m1(); } void accessEnclosingClass2() { m2(); } } } 

Liste 1 déclare une classe de niveau supérieur nommé EnclosingClassavec le champ de la classe s, les méthodes de classe m1()et m2(), et classe membre statique SMClass. SMClassdéclare la méthode de classe accessEnclosingClass()et la méthode d'instance accessEnclosingClass2(). Notez ce qui suit:

  • m2()L'appel de SMClassla accessEnclosingClass()méthode de 's nécessite le SMClass.préfixe car accessEnclosingClass()est déclaré static.
  • accessEnclosingClass()est capable d'accéder EnclosingClassau schamp de et d'appeler sa m1()méthode, même si les deux ont été déclarés private.

Le listing 2 présente le code source à une SMCDemoclasse d'application qui montre comment invoquer SMClassla accessEnclosingClass()méthode de. Il montre également comment instancier SMClasset appeler sa accessEnclosingClass2()méthode d'instance.

Listing 2. Appel des méthodes d'une classe membre statique (SMCDemo.java)

 public class SMCDemo { public static void main(String[] args) { EnclosingClass.SMClass.accessEnclosingClass(); EnclosingClass.SMClass smc = new EnclosingClass.SMClass(); smc.accessEnclosingClass2(); } } 

Comme indiqué dans le listing 2, si vous souhaitez appeler la méthode d'une classe de niveau supérieur à partir d'une classe incluse, vous devez préfixer le nom de la classe incluse avec le nom de sa classe englobante. De même, pour instancier une classe incluse, vous devez préfixer le nom de cette classe avec le nom de sa classe englobante. Vous pouvez ensuite appeler la méthode d'instance de la manière habituelle.

Compilez les listes 1 et 2 comme suit:

 javac *.java 

Lorsque vous compilez une classe englobante qui contient une classe membre statique, le compilateur crée un fichier de classe pour la classe membre statique dont le nom se compose du nom de sa classe englobante, d'un caractère de signe dollar et du nom de la classe membre statique. Dans ce cas, la compilation aboutit à EnclosingClass$SMCClass.classet EnclosingClass.class.

Exécutez l'application comme suit:

 java SMCDemo 

Vous devez observer la sortie suivante:

 Called from SMClass's accessEnclosingClass() method Called from SMClass's accessEnclosingClass() method 

Exemple: classes statiques et Java 2D

La bibliothèque de classes standard de Java est une bibliothèque d'exécution de fichiers de classe, qui stockent les classes compilées et d'autres types de référence. La bibliothèque comprend de nombreux exemples de classes de membres statiques, dont certaines se trouvent dans les classes de formes géométriques Java 2D situées dans le java.awt.geompackage. (Vous en apprendrez plus sur les packages dans le prochain didacticiel Java 101. )

La Ellipse2Dclasse trouvée dans java.awt.geomdécrit une ellipse, qui est définie par un rectangle de cadrage en termes d'un coin supérieur gauche (x, y) avec des étendues de largeur et de hauteur. Le fragment de code suivant montre que l'architecture de cette classe est basée sur Floatet Doubleclasses de membres statiques, les deux sous - classe Ellipse2D:

 public abstract class Ellipse2D extends RectangularShape { public static class Float extends Ellipse2D implements Serializable { public float x, y, width, height; public Float() { } public Float(float x, float y, float w, float h) { setFrame(x, y, w, h); } public double getX() { return (double) x; } // additional instance methods } public static class Double extends Ellipse2D implements Serializable { public double x, y, width, height; public Double() { } public Double(double x, double y, double w, double h) { setFrame(x, y, w, h); } public double getX() { return x; } // additional instance methods } public boolean contains(double x, double y) { // ... } // additional instance methods shared by Float, Double, and other // Ellipse2D subclasses } 

Les classes Floatet Doubles'étendent Ellipse2D, fournissant des Ellipse2Dimplémentations en virgule flottante et en virgule flottante double précision . Les développeurs utilisent Floatpour réduire la consommation de mémoire, en particulier parce que vous pourriez avoir besoin de milliers ou plus de ces objets pour construire une seule scène 2D. Nous utilisons Doublequand une plus grande précision est requise.

Vous ne pouvez pas instancier la Ellipse2Dclasse abstraite , mais vous pouvez instancier soit Floatou Double. Vous pouvez également vous étendre Ellipse2Dpour décrire une forme personnalisée basée sur une ellipse.

À titre d'exemple, disons que vous souhaitez introduire une Circle2Dclasse, qui n'est pas présente dans le java.awt.geompackage. Le fragment de code suivant montre comment créer un Ellipse2Dobjet avec une implémentation en virgule flottante:

 Ellipse2D e2d = new Ellipse2D.Float(10.0f, 10.0f, 20.0f, 30.0f); 

Le fragment de code suivant montre comment créer un Ellipse2Dobjet avec une implémentation en virgule flottante double précision:

 Ellipse2D e2d = new Ellipse2D.Double(10.0, 10.0, 20.0, 30.0); 

Vous pouvez maintenant invoquer l'une des méthodes déclarées dans Floatou Doubleen invoquant la méthode sur la Ellipse2Dréférence retournée (par exemple, e2d.getX()). De la même manière, vous pouvez invoquer l'une des méthodes communes à Floatet Doubleet déclarées dans Ellipse2D. Un exemple est:

 e2d.contains(2.0, 3.0) 

Cela termine l'introduction aux classes membres statiques. Ensuite, nous examinerons les classes internes, qui sont des classes membres non statiques, des classes locales ou des classes anonymes. Vous apprendrez à travailler avec les trois types de classes internes.

télécharger Obtenir le code Téléchargez le code source pour des exemples dans ce didacticiel. Créé par Jeff Friesen pour JavaWorld.

Inner classes, type 1: Non-static member classes

You've learned previously in the Java 101 series how to declare non-static (instance) fields, methods, and constructors as members of a class. You can also declare non-static member classes, which are nested non-static classes that you declare at the same level as instance fields, methods, and constructors. Consider this example:

 class C { int f; void m() {} C() { f = 2; } class D { // members } } 

Here, we introduce top-level class C with instance field f, instance method m(), a constructor, and non-static member class D. All of these entities are members of class C, which encloses them. However, unlike in the previous example, these instance entities are associated with instances ofC and not with the C class itself.

Each instance of the non-static member class is implicitly associated with an instance of its enclosing class. The non-static member class's instance methods can call the enclosing class's instance methods and access its instance fields. To demonstrate this access, Listing 3 declares an EnclosingClass with a nested NSMClass.

Listing 3. Declare an enclosing class with a nested non-static member class (EnclosingClass.java, version 2)

 class EnclosingClass { private String s; private void m() { System.out.println(s); } class NSMClass { void accessEnclosingClass() { s = "Called from NSMClass's accessEnclosingClass() method"; m(); } } } 

Listing 3 declares a top-level class named EnclosingClass with instance field s, instance method m(), and non-static member class NSMClass. Furthermore, NSMClass declares instance method accessEnclosingClass().

Because accessEnclosingClass() is non-static, NSMClass must be instantiated before this method can be called. This instantiation must take place via an instance of EnclosingClass, as shown in Listing 4.

Listing 4. NSMCDemo.java

 public class NSMCDemo { public static void main(String[] args) { EnclosingClass ec = new EnclosingClass(); ec.new NSMClass().accessEnclosingClass(); } } 

Listing 4's main() method first instantiates EnclosingClass and saves its reference in local variable ec. The main() method then uses the EnclosingClass reference as a prefix to the new operator, in order to instantiate NSMClass. The NSMClass reference is then used to call accessEnclosingClass().

Should I use 'new' with a reference to the enclosing class?

Prefixing new with a reference to the enclosing class is rare. Instead, you will typically call an enclosed class's constructor from within a constructor or an instance method of its enclosing class.

Compile Listings 3 and 4 as follows:

 javac *.java 

When you compile an enclosing class that contains a non-static member class, the compiler creates a class file for the non-static member class whose name consists of its enclosing class's name, a dollar-sign character, and the non-static member class's name. In this case, compiling results in EnclosingClass$NSMCClass.class and EnclosingClass.class.

Run the application as follows:

 java NSMCDemo 

You should observe the following output:

 Called from NSMClass's accessEnclosingClass() method 

When (and how) to qualify 'this'

An enclosed class's code can obtain a reference to its enclosing-class instance by qualifying reserved word this with the enclosing class's name and the member access operator (.). For example, if code within accessEnclosingClass() needed to obtain a reference to its EnclosingClass instance, it would specify EnclosingClass.this. Because the compiler generates code to accomplish this task, specifying this prefix is rare.

Example: Non-static member classes in HashMap

The standard class library includes non-static member classes as well as static member classes. For this example, we'll look at the HashMap class, which is part of the Java Collections Framework in the java.util package. HashMap, which describes a hash table-based implementation of a map, includes several non-static member classes.

For example, the KeySet non-static member class describes a set-based view of the keys contained in the map. The following code fragment relates the enclosed KeySet class to its HashMap enclosing class:

 public class HashMap extends AbstractMap implements Map, Cloneable, Serializable { // various members final class KeySet extends AbstractSet { // various members } // various members } 

The and syntaxes are examples of generics, a suite of related language features that help the compiler enforce type safety. I'll introduce generics in an upcoming Java 101 tutorial. For now, you just need to know that these syntaxes help the compiler enforce the type of key objects that can be stored in the map and in the keyset, and the type of value objects that can be stored in the map.

HashMapfournit une keySet()méthode qui instancie KeySetsi nécessaire et renvoie cette instance ou une instance mise en cache. Voici la méthode complète: