【Java】 匿名内部クラス / 無名内部クラス

■ はじめに

 以下の関連記事
https://blogs.yahoo.co.jp/dk521123/32156111.html
https://blogs.yahoo.co.jp/dk521123/36602948.html
にあるように、List や Map を初期化するのに、以下のように行っていた。
~~~~~~~
List<Person> list = new ArrayList<Person>() {
    {
        add(person1);
        add(person2);
    }
};
~~~~~~~

ただ、以下「問題が発生したソース」のように組んだら
例外が発生して意図した通りに動かなかった。
結論から言うと、上記のように匿名内部クラスを利用してリストを初期化したのが原因でだったのだが
Java文法的に理解を深めるために、この「匿名内部クラス」を扱う。

ちなみに、以下の説明が分かり易いかも。
http://ichitcltk.hustle.ne.jp/gudon2/index.php?pageType=file&id=java009_AnonymousClass

問題が発生したケース1

Person.java
import java.io.Serializable;

public class Person implements Serializable {
  private static final long serialVersionUID = 1L;
  
  private int id;
  private String name;

  public Person() {
    this(-1, null);
  }

  public Person(int id, String name) {
    this.id = id;
    this.name = name;
  }

  public int getId() {
    return this.id;
  }

  public void setId(int id) {
    this.id = id;
  }

  public String getName() {
    return this.name;
  }

  public void setName(String name) {
    this.name = name;
  }
}
Main.java
import java.beans.XMLEncoder;
import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;

public class Main {

  public static void main(String[] args) {
    Main main = new Main();
    main.testNG();
    
    System.out.println();
    
    main.testOK();
  }

  public void testNG() {
    System.out.println("Start - testNG()");

    // NG case ★注目★(Arrays.asList()を使っても同じ結果だった)
    List<Person> listNG = new ArrayList<Person>() {
      private static final long serialVersionUID = 1L;
      {
        add(new Person(1, "Mike"));
        add(new Person(2, "Tom"));
      }
    };

    saveXml(listNG);

    System.out.println("End - testNG()");
  }

  public void testOK() {
    System.out.println("Start - testOK()");

    // OK case ★注目★
    List<Person> listOK = new ArrayList<Person>();
    listOK.add(new Person(1, "Mike"));
    listOK.add(new Person(2, "Tom"));

    saveXml(listOK);

    System.out.println("End - testOK()");
  }

  public static <T> void saveXml(T objectToSave) {
    try (XMLEncoder xMLEncoder = new XMLEncoder(new BufferedOutputStream(new FileOutputStream("test.xml")));) {
      xMLEncoder.writeObject(objectToSave);
    } catch (FileNotFoundException e) {
      e.printStackTrace();
    }
  }
}
出力結果
Start - testNG()
java.lang.InstantiationException: com.sample.anonymousinner.Main$1
Continuing ...
java.lang.Exception: XMLEncoder: discarding statement XMLEncoder.writeObject(Main$1);
Continuing ...
End - testNG()

Start - testOK()
End - testOK()

問題が発生したケース2

https://blogs.yahoo.co.jp/dk521123/32813165.html
の「使用上の注意」を参照のこと

■ 匿名内部クラス (Anonymous Inner Class) / 無名内部クラス

 * 名前をつけることなく、クラス定義してインスタンスを生成したクラス

■ 構文

// [匿名クラスの親クラス]は、インターフェイスでもOK
new 匿名クラスの親クラス() {
   内部クラスのメソッド、データ
}
初期化子付き
new 匿名クラスの親クラス() {
   {
      初期化子(イニシャライザ/initializer)の処理
   }
   内部クラスのメソッド、データ
}

匿名内部クラスを使ったListの初期化について考える

上記「問題が発生したソース」のNGケースを考える
// インスタンス変数『listNG』は、『ArrayList<Person>クラスを継承した匿名クラスのインスタンス』★今日の収穫★
//  => 『listNG』は、決して List<Person> のインスタンスではない
List<Person> listNG = new ArrayList<Person>() { // [匿名クラスの親クラス] => ArrayList<Person>
  {
    // 初期化子を利用して、リストにインスタンスを追加している
    add(new Person(1, "Mike"));
    add(new Person(2, "Tom"));
  }
};


関連記事

Java】メソッド参照

https://blogs.yahoo.co.jp/dk521123/37204926.html

Java】コレクション ~ List 編~

https://blogs.yahoo.co.jp/dk521123/32156111.html

Java】 コレクション ~ Map編 / あれこれ ~

https://blogs.yahoo.co.jp/dk521123/36602948.html