#入れ子クラス(Nested Class)
-
入れ子クラス(Nested Class)
- staticメンバークラス
-
内部クラス(Inner Class)
- 非staticメンバークラス
- 匿名クラス
##staticメンバークラス
- 特定クラス(MyClass)にクラス(MyHelper)が依存、MyHelperはMyClassからしか呼ばれない
- →1個の.javaファイルにまとめることを考える
- トップレベルクラス:ネストしてないファイル直下のクラス
-
エンクロージングクラス:入れ子のクラスを内側に含んだクラス
- 親クラスX:エンクロージングクラスかつトップレベルクラス
- 子クラスY:ZのエンクロージングクラスでXの入れ子クラス
- 孫クラスZ:Yの入れ子クラス
public class MyClass{...}
class MyHelper{...}
- しかし、MyHelperはあくまでパッケージプライベートで、同じパッケージからは自由にアクセスできる
- →staticでMyClassのプライベートにすべき!
- エンクロージングクラスのclassブロック配下で入れ子クラス定義すると
- 入れ子クラスはエンクロージングクラス外からは見えない
- 入れ子クラスの名前は**
エンクロージングクラス.入れ子クラス(MyClass.MyHelper)
**- エンクロージング内部ではメンバークラス名で呼び出し可能
-
import (package名).MyClass.MyHelper;
でエンクロージング外からもvar h = new MyHelper();
可能
//classブロック配下でMyHelper定義
public class MyClass {
//staticメンバークラスの定義
//メンバーはMyClass外からは見えない
private static class MyHelper {
public void show() {
System.out.println("Nested class is running!");
}
}
public void run() {
//エンクロージング内部ではMyHelper呼び出し可能
var helper = new MyHelper();
helper.show();
}
}
public class NestBasic {
public static void main(String[] args) {
var c = new MyClass();
c.run();
//MyHelpeはMyClass外からは見えない
// var h = new MyClass.MyHelper(); //エラー!
}
}
##非staticメンバークラス(=内部クラス)
- static修飾子なし
- 内部クラス:エンクロージングオブジェクトに所属
- staticメンバークラス:エンクロージングクラスに所属
- 内部クラスの特徴
- 内部クラスをインスタンス化するにはエンクロージングオブジェクト必要
- 内部クラスからはthis変数経由でエンクロージングクラスメンバーにアクセス
- エンクロージングクラスのインスタンスフィールド参照用途に使用
- 個々のインスタンスがエンクロージングオブジェクトへの参照を持つのでメモリを消費する
class MyClass {
private String str1 = "エンクロージング・インスタンス";
private static String str2 = "エンクロージング・クラス";
private class MyHelper {
private String str1 = "ネスト・インスタンス";
private static final String str2 = "ネスト・クラス";
public void show() {
//内部クラスはエンクロージングオブジェクトへの参照を持つ
//MyClass.thisでアクセス可
System.out.println(MyClass.this.str1); //エンクロージング・インスタンス
System.out.println(MyClass.str2); //エンクロージング・クラス
}
}
public void run() {
//内部クラスのインスタンスはエンクロージングクラスのインスタンスメソッドで生成
var helper = new MyHelper();
helper.show();
//エンクロージングオブジェクトから内部クラスにアクセス
System.out.println(helper.str1); //ネスト・インスタンス
System.out.println(MyHelper.str2); //ネスト・クラス
}
}
public class NestedAccess {
public static void main(String[] args) {
var c = new MyClass();
c.run();
}
}
##非staticメンバークラス(内部クラス)の実例
- private内部クラスを宣言したらエンクロージング外からは見えない
- 見えなくなるのは型名のみ、インスタンスの参照は制限なし
java.util.AbstractList.java
package java.util;
import java.util.function.Consumer;
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
//...中略
/*Iterators
Itrクラスの内部的な実装を見せる必要はない
外部からは実装元であるIterators型が見えたらいい
iteratorメソッドの戻り値はIterator型
*/
public Iterator<E> iterator() {
return new Itr();
}
//...中略
/*内部クラス
リスト内のprivateフィイールドにアクセス、配下要素を順にアクセスするイテレータを実装
イテレータはIterator実装クラスとして別に定義する
*/
private class Itr implements Iterator<E> {
//...中略
}
//...中略
}
##匿名クラス(無名クラス)
- 特定の処理をまとめて表すための器として使う
- ex:イベントリスナー
- 特定の文脈(ユーザーの動作など)に強く関連するので他では再利用しない
-
new 基底クラス(引数,...){クラスの本体}
new View.OnClickListener(){
- 匿名クラスには名前がないのでnew演算子に基底クラス/インターフェースの名前を指定
- 用意したリスナーのインスタンスにはそのままメソッドに渡せる
- 注意;
- コンストラクタを持てない(初期化はOK)
- 継承不可(暗黙的にfinal)
- 抽象クラス宣言不可
//イベントリスナー登録
btn.setOnClickListener(
//匿名クラス(イベントリスナー)定義
new View.OnClickListener(){
//btnクリックでtxtに現在時刻を表示
@Override
public void onClick(View view){
TextView txt = findViewById(R.id.txtResult);
txt.setText(LocalDateTime.now().toString());
}
}
);
##ローカルクラス
- メソッド、コンストラクタなどブロック配下で定義されたクラス
- クラスが特定のメソッド、コンストラクタで利用されるときに使う
- 匿名クラスで代用OK