Javaの静的クラスと内部クラス

ネストされたクラスは、他のクラスまたはスコープのメンバーとして宣言されているクラスです。クラスのネストは、コードをより適切に整理する1つの方法です。たとえば、オブジェクトをサイズ変更可能な配列に格納するネストされていないクラス(トップレベルクラスとも呼ばれます)があり、その後に各オブジェクトを返すイテレータクラスがあるとします。トップレベルクラスの名前空間を汚染するのではなく、サイズ変更可能な配列コレクションクラスのメンバーとしてイテレータクラスを宣言できます。これは、2つが密接に関連しているために機能します。

Javaでは、ネストされたクラスは静的メンバークラスまたは内部クラスのいずれかに分類されます。内部クラスは、非静的メンバークラス、ローカルクラス、または匿名クラスです。このチュートリアルでは、Javaコードで静的メンバークラスと3種類の内部クラスを操作する方法を学習します。

ネストされたクラスでのメモリリークを回避する

このチュートリアルに関連するJavaのヒントも参照してください。ここでは、ネストされたクラスがメモリリークに対して脆弱である理由を学習します。

Javaの静的クラス

私のJava101チュートリアルJavaのクラスとオブジェクトでは、静的フィールドと静的メソッドをクラスのメンバーとして宣言する方法を学びました。 Javaでのクラスとオブジェクトの初期化では、静的初期化子をクラスのメンバーとして宣言する方法を学びました。次に、静的クラスを宣言する方法を学習します。正式には静的メンバークラスと呼ばれ、これらは、staticキーワードを使用して、これらの他の静的エンティティと同じレベルで宣言するネストされたクラスです。静的メンバークラス宣言の例を次に示します。

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

この例ではC、静的フィールドf、静的メソッドm()、静的初期化子、および静的メンバークラスを持つトップレベルクラスを紹介しますDDがのメンバーであることに注意してくださいC。静的フィールドf、静的メソッドm()、および静的初期化子ものメンバーですC。これらの要素はすべてクラスCに属しているため、それを囲むクラスと呼ばれます。クラスDは、囲まれたクラスとして知られてます。

エンクロージャとアクセスルール

囲まれていても、静的メンバークラスは、囲んでいるクラスのインスタンスフィールドにアクセスして、そのインスタンスメソッドを呼び出すことはできません。ただし、宣言されているメンバーであっても、囲んでいるクラスの静的フィールドにアクセスして静的メソッドを呼び出すことはできますprivate。実例を示すために、リスト1EnclosingClassはネストされたを使用してを宣言していSMClassます。

リスト1.静的メンバークラスの宣言(EncloseingClass.java、バージョン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(); } } } 

リスト1はEnclosingClass、クラスフィールドs、クラスメソッドm1()m2()、および静的メンバークラスで名前が付けられたトップレベルクラスを宣言していSMClassます。SMClassクラスメソッドaccessEnclosingClass()とインスタンスメソッドを宣言しますaccessEnclosingClass2()。次の点に注意してください。

  • m2()が宣言されているため、SMClassaccessEnclosingClass()メソッドの呼び出しにはSMClass.プレフィックスが必要です。accessEnclosingClass()static
  • accessEnclosingClass()アクセスすることが可能であるEnclosingClasssフィールドとその呼び出しm1()の両方が宣言されているにもかかわらず、方法をprivate

リスト2は、のメソッドSMCDemoを呼び出す方法を示すアプリケーションクラスへのソースコードを示しています。また、インスタンスメソッドをインスタンス化して呼び出す方法も示します。SMClassaccessEnclosingClass()SMClassaccessEnclosingClass2()

リスト2.静的メンバークラスのメソッドの呼び出し(SMCDemo.java)

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

リスト2に示すように、囲まれたクラス内から最上位クラスのメソッドを呼び出す場合は、囲まれたクラスの名前の前に、それを囲むクラスの名前を付ける必要があります。同様に、囲まれたクラスをインスタンス化するには、そのクラスの名前の前に、それを囲むクラスの名前を付ける必要があります。その後、通常の方法でインスタンスメソッドを呼び出すことができます。

リスト1と2を次のようにコンパイルします。

 javac *.java 

静的メンバークラスを含む包含クラスをコンパイルすると、コンパイラーは、その名前が包含クラスの名前、ドル記号文字、および静的メンバークラスの名前で構成される静的メンバークラスのクラスファイルを作成します。この場合、コンパイルするEnclosingClass$SMCClass.classととになりEnclosingClass.classます。

次のようにアプリケーションを実行します。

 java SMCDemo 

次の出力を確認する必要があります。

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

例:静的クラスとJava 2D

Java's standard class library is a runtime library of class files, which store compiled classes and other reference types. The library includes numerous examples of static member classes, some of which are found in the Java 2D geometric shape classes located in the java.awt.geom package. (You'll learn about packages in the next Java 101 tutorial.)

The Ellipse2D class found in java.awt.geom describes an ellipse, which is defined by a framing rectangle in terms of an (x,y) upper-left corner along with width and height extents. The following code fragment shows that this class's architecture is based on Float and Double static member classes, which both subclass 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 } 

The Float and Double classes extend Ellipse2D, providing floating-point and double precision floating-point Ellipse2D implementations. Developers use Float to reduce memory consumption, particularly because you might need thousands or more of these objects to construct a single 2D scene. We use Double when greater accuracy is required.

You cannot instantiate the abstract Ellipse2D class, but you can instantiate either Float or Double. You also can extend Ellipse2D to describe a custom shape that's based on an ellipse.

As an example, let's say you want to introduce a Circle2D class, which isn't present in the java.awt.geom package. The following code fragment shows how you would create an Ellipse2D object with a floating-point implementation:

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

次のコードフラグメントはEllipse2D、倍精度浮動小数点実装を使用してオブジェクトを作成する方法を示しています。

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

あなたは今のいずれかの方法がで宣言呼び出すことができるFloatか、Double返された上でメソッドを呼び出すことでEllipse2D、参照(例えば、e2d.getX())。同様に、FloatDoubleに共通で、で宣言されている任意のメソッドを呼び出すことができますEllipse2D。例は次のとおりです。

 e2d.contains(2.0, 3.0) 

これで静的メンバークラスの紹介は終わりです。次に、非静的メンバークラス、ローカルクラス、または匿名クラスである内部クラスについて説明します。3つの内部クラスタイプすべてを操作する方法を学習します。

ダウンロードコードを入手するこのチュートリアルの例のソースコードをダウンロードします。JavaWorld用にJeffFriesenによって作成されました。

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.

HashMap必要に応じkeySet()てインスタンス化しKeySet、このインスタンスまたはキャッシュされたインスタンスを返すメソッドを提供します。完全な方法は次のとおりです。