Javaの内部クラス
Javaの内部クラスは、他のクラスの本体の中に定義されます。Javaの内部クラスは、private、public、protected、またはデフォルトのアクセスで宣言することができますが、外部クラスはpublicまたはデフォルトのアクセスしか持つことができません。Javaのネストしたクラスは、2つのタイプに分けられます。
-
- 静的なネストされたクラス
-
- ネストされたクラスが静的であれば、それは静的なネストされたクラスと呼ばれます。静的なネストされたクラスは、外部クラスの静的メンバーにのみアクセスできます。静的なネストされたクラスは他のトップレベルクラスと同じであり、パッケージングの便宜のためにのみネストされています。静的なクラスオブジェクトは以下のステートメントで作成できます。
-
- OuterClass.StaticNestedClass nestedObject =
-
- new OuterClass.StaticNestedClass();
Javaのインナークラス
非静的なネストされたクラスはJavaではインナークラスと呼ばれます。Javaのインナークラスはクラスのオブジェクトと関連付けられており、外部クラスのすべての変数とメソッドにアクセスできます。インナークラスはインスタンスに関連付けられているため、それらに静的な変数は持つことができません。Javaのインナークラスのオブジェクトは外部クラスのオブジェクトの一部であり、インナークラスのインスタンスを作成するには、まず外部クラスのインスタンスを作成する必要があります。Javaのインナークラスは次のようにインスタンス化することができます。
OuterClass outerObject = new OuterClass();
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
Javaには2つの特別な種類の内部クラスがあります。
-
- ローカルインナークラス
-
- メソッドの本体でクラスが定義されている場合、それはローカルインナークラスと呼ばれます。ローカルインナークラスはオブジェクトと関連付けられていないため、private、public、protectedのアクセス修飾子を使用することはできません。許可されている修飾子は抽象またはfinalのみです。ローカルインナークラスは、定義されているスコープ内の包括クラスとローカルなfinal変数のすべてのメンバーにアクセスすることができます。さらに、メソッド内で定義された非finalのローカル変数にもアクセスすることができますが、変更することはできません。したがって、非finalのローカル変数の値を印刷しようとすると許可されますが、メソッド内のローカルインナークラスから値を変更しようとするとコンパイル時エラーが発生します。ローカルインナークラスは次のように定義できます:
パッケージcom.scdev.innerclasses;
publicクラスMainClass {
private String s_main_class;
public void print() {
String s_print_method = “”;
// メソッド内のローカルインナークラス
class Logger {
// 包括クラスの変数にアクセスできる
String name = s_main_class;
// 非finalのメソッド変数にアクセスできる
String name1 = s_print_method;
public void foo() {
String name1 = s_print_method;
// 以下のコードはコンパイル時エラーが発生します:
// 状況によっては、近くのスコープで定義されたローカル変数s_print_methodはfinalまたは効果的にfinalである必要があります
// s_print_method= “:”;
}
}
// メソッド内でローカルインナークラスをインスタンス化して使用する
Logger logger = new Logger();
}
}
ブロック内にもローカルインナークラスを定義することができます。例えば、staticブロック、if-elseブロックなどです。ただし、この場合、クラスのスコープは非常に限られます。
publicクラス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();
}
}
匿名インナークラス
名前のないローカルインナークラスは匿名インナークラスとして知られています。匿名クラスは定義とインスタンス化を1つの文で行います。匿名インナークラスは常にクラスを拡張するか、インターフェイスを実装します。匿名クラスには名前がないため、匿名クラスのためのコンストラクタを定義することはできません。匿名インナークラスは、定義されたポイントでのみアクセス可能です。匿名インナークラスの作成方法を具体的な例で見てみましょう。
以下は、Javaの内部クラス、静的ネストクラス、ローカル内部クラス、匿名内部クラスを定義する方法を示したJavaクラスです。OuterClass.java
package com.scdev.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 constructor
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;
}
//static nested class, can access OuterClass static variables/methods
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;
}
}
//inner class, non-static and can access all the variables/methods of the outer class
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;
}
}
//local inner class
public void print(String initial) {
//local inner class 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);
}
//anonymous inner class
public String[] getFilesInDir(String dir, final String ext) {
File file = new File(dir);
//anonymous inner class 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.scdev.nested;
import java.util.Arrays;
//nested classes can be used in import for easy instantiation
import com.scdev.nested.OuterClass.InnerClass;
import com.scdev.nested.OuterClass.StaticNestedClass;
public class InnerClassTest {
public static void main(String[] args) {
OuterClass outer = new OuterClass(1,2,3,4);
//static nested classes example
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);
//inner class example
InnerClass innerClass = outer.new InnerClass();
System.out.println(innerClass.getName());
System.out.println(innerClass);
innerClass.setValues();
System.out.println(innerClass);
//calling method using local inner class
outer.print("Outer");
//calling method using anonymous inner class
System.out.println(Arrays.toString(outer.getFilesInDir("src/com/scdev/nested", ".java")));
System.out.println(Arrays.toString(outer.getFilesInDir("bin/com/scdev/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内部クラスの利点
-
- 1つのクラスにのみ有用な場合、そのクラスをネストしてまとめておくことが意味があります。それはクラスのパッケージングに役立ちます。
-
- Javaの内部クラスはカプセル化を実現します。内部クラスは外部クラスのprivateメンバーにアクセスできると同時に、外部の世界から内部クラスを隠すこともできます。
- 小さなクラスをトップレベルのクラス内に保持することで、コードが使用される場所に近くなり、コードがより読みやすくメンテナンスしやすくなります。
Javaの内部クラスに関する説明は以上です。