スタートページJavascriptJava

Javaシリーズの目次

Javaの文法(7) クラスの入れ子


入れ子クラスの一般形

クラスの中にクラスを入れ子にすることができます。親クラスをエンクロージングクラス、子クラスをメンバークラスといいます。
入れ子クラスの一般形を示します。

class 親クラス名(エンクロージングクラス名) {
  class 子クラス名(メンバークラス名) {
    ...メンバークラスのメソッド...
  }
  ...エンクロージングクラスのメソッド...
}
public class NestBasic {
  public static void main(String[] args) {
  ... main メソッド...
  }
}

入れ子クラスの種類

  staticメンバークラス:子メソッドがstatic宣言されているもの
  非staticメンバークラス:static宣言されていないもの
  匿名クラス(無名クラス):名前を持たないクラス
  ローカルクラス:メソッド配下で宣言されたクラス(他のクラスは親クラスの直下にある)

staticと非static

子クラスを離れて、Main クラス内に 計算メソッドがあり、それが staticメソッドか否かにより、どのような違いがあるかを説明します。

staticのとき

public class Main {
  public static void main(String[] args) {
    int x = 10;
    int y = 20;
    int z = 計算(x, y);
    System.out.println(z);
  }
  public static int 計算(int x, int y) {
     return x+y;
  }
}

ここで、static を外すとエラーになります。staticなメソッド(main)から同一クラス(Main)内の非staticなメソッド(計算)への直接なアクセスはできないからです。

非staticのときは、次にようにインスタンス化をする必要があります。

public class Main {
  public static void main(String[] args) {
    int x = 10;
    int y = 20;
    Main インスタンス = new Main();  // Mainクラスのインスタンスを生成
    int z = インスタンス.計算(x ,y); // 関数「計算」の呼び出し
    System.out.println(z);
  }
  public int 計算(int x, int y) {
     return x+y;
  }
}

staticメンバークラス

例1

class 親クラス {  // エンクロージングのクラス
  int a = 10;

  public void 親メソッド() {        // public なので Main から呼び出せる
    int b = 20;
    System.out.println("a:親クラスで定義 = " + a);    // a:親クラスで定義 = 10
    System.out.println("b:親メソッドで定義 = " + b);  // b:親メソッド定義 = 20
//  System.out.println("c:子クラスで定義 = " + c);    // エラー 
//  System.out.println("d:子メソッドで定義 = " + d);  // エラー 
    子クラス オブジェクト = new 子クラス();
    オブジェクト.子メソッド();                        // 子メソッドを呼び出す
  }
  private static class 子クラス {    // staticメンバークラス。private なので親クラスからは呼び出せるが、Main からは不可
    int c = 30;
    public void 子メソッド() {
      int d = 40;
//    System.out.println("a:親クラスで定義 = " + a);    // エラー
//    System.out.println("b:親メソッドで定義 = " + b);  // エラー
      System.out.println("c:子クラスで定義 = " + c);    // c:子クラスで定義 = 30 
      System.out.println("d:子メソッドで定義 = " + d);  // d:子メソッドで定義 = 40
    }
  }
}

public class Main {
  public static void main(String args[]){
    // 親メソッドが 非static なのでインスタンス化が必要
    親クラス オブジェクト = new 親クラス();
    オブジェクト.親メソッド();  // 親クラスは呼び出せる。子メソッドは private なので呼び出せない
  }
}

例2 親メソッドから子メソッドへのデータ渡し

class 親クラス {
  int a = 10;

  public void 親メソッド() {
    int b = 20;
    子クラス オブジェクト = new 子クラス();
    オブジェクト.子メソッド(a, b);         // 変数の送出
  }
  private static class 子クラス {
    int c = 30;
    public void 子メソッド(int a, int b){  // 変数の受取
      int d = 40;
      System.out.println("a:親クラスで定義 = " + a);    // a:親クラスで定義 = 10
      System.out.println("b:親メソッドで定義 = " + b);  // b:親メソッドで定義 = 20
      System.out.println("c:子クラスで定義 = " + c);    // c:子クラスで定義 = 30 
      System.out.println("d:子メソッドで定義 = " + d);  // d:子メソッドで定義 = 40
      System.out.println("a+b+c+d = " + (a+b+c+d));     // a+b+c+d = 100
    }
  }
}

public class Main {
  public static void main(String args[]){
    親クラス オブジェクト = new 親クラス();
    オブジェクト.親メソッド();
  }
}

非staticメンバークラス

例1 Main で親メソッドと子メソッドのインスタンス化をする場合

class 親クラス {
  int a = 10;

  public void 親メソッド() {
    int b = 20;
      System.out.println("親メソッドでの出力");
      System.out.println("a:親クラスで定義 = " + a);    // 親クラスで定義 = 10
      System.out.println("b:親メソッドで定義 = " + b);  // b:親メソッドで定義 = 20
//    System.out.println("c:子クラスで定義 = " + c);    // エラー
//    System.out.println("d:子メソッドで定義 = " + d);  // エラー
  }
  class 子クラス {
    int c = 30;
    public void 子メソッド() {
      int d = 40;
      System.out.println("子メソッドでの出力");
      System.out.println("a:親クラスで定義 = " + a);    // a:親クラスで定義 = 10
//    System.out.println("b:親メソッドで定義 = " + b);  // エラー
      System.out.println("c:子クラスで定義 = " + c);    // c:子クラスで定義 = 30 
      System.out.println("d:子メソッドで定義 = " + d);  // d:子メソッドで定義 = 40
    }
  };
}

public class Main {
  public static void main(String args[]){
    親クラス 親オブジェクト = new 親クラス();
    親オブジェクト.親メソッド();                         // 「親メソッドでの出力」だけ
    親クラス.子クラス 子オブジェクト = 親オブジェクト.new 子クラス();
    子オブジェクト.子メソッド();                         // 「子メソッドでの出力」だけ
  }
}
出力順序
  親メソッドでの出力
  a:親クラスで定義 = 10
  b:親メソッドで定義 = 20
  子メソッドでの出力
  a:親クラスで定義 = 10
  c:子クラスで定義 = 30
  d:子メソッドで定義 = 40

例2 親メソッドで子メソッドのインスタンス化をする場合

class 親クラス {
  private String 文字列I = "親クラス定義";
  private static String 文字列C = "親クラス定義・static";
  public void 親メソッド() {
    //内部クラスのインスタンスは親クラスのインスタンスメソッドで生成
    子クラス 子結果 = new 子クラス();
    子結果.子メソッド();

    //親クラス定義オブジェクトから内部クラスにアクセス
    System.out.println(子結果.文字列I);    // 子クラス定義
    System.out.println(子クラス.文字列C);  // 子クラス定義・static
  }

  private class 子クラス {                  // private:親クラスからは見えるが、Main からは見えない
    private String 文字列I = "子クラス定義";
    private static final String 文字列C = "子クラス定義・static";
    public void 子メソッド() {
      //内部クラスは親クラス定義オブジェクトへの参照を持つ
      //親クラス.thisでアクセス可
      System.out.println(親クラス.this.文字列I); //親クラス定義
      System.out.println(親クラス.文字列C); //親クラス定義・static
    }
  }
}

public class Main {
  public static void main(String[] args) {
    // 子クラスは、private なので、Main からは見えない。
    親クラス 親結果 = new 親クラス();
    親結果.親メソッド();
  }
}

https://aki-bunkei.com/anonymous-class/

匿名クラス

抽象クラス

public interface 抽象クラス {
  public static final double π = 3.14;
  void 結果();
}
public class 実装クラス implements 抽象クラス {
  // 具体的な処理
}

匿名クラスは、無名クラスともいいます。
親クラスまたはインターフェイスの名前の無いサブクラスをその場で宣言して利用する、というイメージです。
実例で理解するほうがよいでしょう。ケース1での「実装」クラスを、ケース2・3では匿名にしています。

ケース1 匿名クラスではない場合(実装クラスの存在)

interface 抽象{
  void 実行();
}

class 実装 implements 抽象 {
  public void 実行() {
    System.out.println("実装の実行");
  }
}

public class Main{
  public static void main(String[] args){
    実装 結果 = new 実装();
    結果.実行();
  }
}

ケース2 実装クラスを匿名クラスにする

匿名クラスで生成したインスタンスを変数「結果」に代入

interface 抽象{
  void 実行();
}

public class Main{
  public static void main(String[] args){
    抽象 インスタンス = new 抽象(){ // メソッドの中でインターフェースを実装してしまう
      public void 実行(){
        System.out.println("抽象の実行");
      }
    };
    インスタンス.実行(); // 実装した実行()メソッドを呼び出し
  }
}

ケース3 同上 変数「結果」も省略

interface 抽象{
  void 実行();
}

public class Main{
  public static void main(String[] args){
    抽象 結果 = new 抽象(){ // メソッドの中でインターフェースを実装してしまう
      public void 実行(){
      System.out.println("B の実行");
      }
    };
    new 抽象(){ // 変数すら省略
      public void 実行(){
        System.out.println("抽象の実行");
      }
    }.実行();    // ; が必要
  }
}

ローカルクラス

親メソッド内で定義するクラスです。
親クラス、親メソッド、子クラス、子メソッドのすべてがアクセス修飾子が省略されています。
一般には、親メソッドに特化したサブルーチンのような使われ方をします。

class 親クラス{
  int a = 10;

  void 親メソッド(){
    int b = 20;

    class 子クラス {        // 子クラスのアクセス修飾子なし。
      int c = 30;
      void 子メソッド() {   // 子メソッドもアクセス修飾子なし。
        int d = 40;
        System.out.println("a:親クラスで定義 = " + a);      // a:親クラスで定義 = 10
        System.out.println("b:親メソッドで定義 = " + b);    // b:親メソッド定義 = 20
        System.out.println("c:子クラスで定義 = " + c);      // c:子メソッドで定義 = 30
        System.out.println("d:子メソッドで定義 = " + d);    // d:子メソッドで定義 = 40
        System.out.println("a+b+c+d=" + (a+b+c+d));         // a+b+c+d=100
      }
    }
    子クラス 子オブジェクト = new 子クラス();
    子オブジェクト.子メソッド();
  }
}

public class Main {
  public static void main(String args[]){
    親クラス オブジェクト = new 親クラス();
    オブジェクト.親メソッド();
  }
}