Java Gold SE11対策 ネストしたクラスシリーズ④ 匿名クラスについて

前回の記事
[Java Gold SE11対策 ネストしたクラスシリーズ③ ローカルクラスについて](https://qiita.com/hiroki1994/items/7a4d53046ed1ae1a6267)
の続きとして本記事では「匿名クラス」についてまとめていく。
今回でこのシリーズは最後となる。


## 匿名クラスとはクラス名を持たないクラス
匿名クラスとは名前を付けずに宣言したクラスの事である。
これはクラス定義とインスタンス化を一つの式で記述している。

クラス名を定義せずにインターフェイスの実装、オーバーライド、クラスの継承を行うことができる。
匿名クラスの中では実装内容だけが定義される。

では、インターフェイスを匿名クラスを使って実現する例を見ていただく。

Testインタフェースではhelloというメソッドが定義されている。

```Test.java
public interface Test {
    void hello();
}
```
続いてSampleクラス。
Testインタフェースを実現した匿名クラスをmainメソッド内で定義している。

コメントのAからBまでが匿名クラスの定義になっているが、
インタフェースの型(Test)でコンストラクタを呼び出し、その後ろを中カッコ{}で囲って、その中でTestインタフェースの抽象メソッドを具体的に実装している。
親クラスのメソッドを匿名クラスを使って定義するなら、親クラスの型でコンストラクタを呼び出す。

```Sample.java
public class Sample {
    public static void main(String[] args) {

        //Testインタフェースを実装した匿名クラスを定義し、同時にインスタンスを生成
        Test test = new Test() {//A
            @Override
            public void hello() {
                System.out.println("Hello");
            }
        }//B
        test.hello();//匿名クラスのメソッドの呼び出し
    }
}

```

インタフェースの場合、インスタンス化できないのでそれを実現したクラスが本来必要になる。
そしてその実現したクラスをインスタンス化してやっとお目当てのメソッドが利用できる。
ただ、クラス内の特定のメソッドのみでしか使われない処理のために、わざわざお目当てのメソッドを持ったインタフェースを実現するクラスを新たに作成して、それをインスタンス化する というのは手間がかかるし、作成したクラスも今後保守管理しなくてはならないので必ずしもその手順がベストというわけではない。

インターフェースやスーパークラスのメソッドをちょっと拝借したいときに、
匿名クラスを利用するとこういった手間が省けるので便利である。

## 独自のメソッドの実装

匿名クラスはインターフェースやスーパークラスにはない**独自のメソッド**を定義するのも可能である。

例えば先ほど例に使ったTestインタフェースにはない、calcメソッドを定義してみよう。

```Sample.java
public class Sample {
    public static void main(String[] args) {

        //Testインタフェースにはないcalcメソッドを実装
        var test = new Test() {
            
            public void calc(){
                System.out.println(1 + 1);
            }
        }
        test.calc();//匿名クラスのメソッドの呼び出し
    }
}

```

ここで注目していただきたいのが、testが**varを使って変数宣言**されていることだ。
「Test test」とするのが普通だが、これだと「**Test型にはcalcなんてメソッドはない」** とコンパイルエラーになってしまうのだ。varを使って変数宣言することで**コンパイラが匿名クラスの定義情報からどのようなメソッドがあるのかを推論してくれる**ので、コンパイルエラーを防ぐことができるのだ。

## コンストラクタは定義できない

通常のクラスにはクラス名と同名のコンストラクタがあって、それを用いてインスタンス化するが、**匿名クラスには名前がないので同名のコンストラクタなど作りようがない**。

コンストラクタは定義できないが、**初期化子**を利用して初期化することはできる。
先ほどのcalcメソッドを実装した例を改変した。
初期化子ではint型のnumという変数を1で初期化している。
```Sample.java
public class Sample {
    public static void main(String[] args) {

        //Testインタフェースにないcalcメソッドを実装
        var test = new Test() {
            //初期化子でint型の変数numを初期化
            {
                int num = 1;
            }
            public void calc(){
                System.out.println(num + 1);
            }
        }
        test.calc();//匿名クラスのメソッドの呼び出し
    }
}

```

## インスタンス化されているもの

下記は最初の例に使用したSampleクラスの実装内容をコピペして一部コメントを変えたものである。
Testは説明用にインターフェースではなくクラスに変更した。

Sampleクラスの「Test test = new Test()」とあり、一見Testクラスをインスタンス化しているようにも見えるが、実際は**Testクラスを継承した匿名のサブクラスのインスタンスを生成している**ことに注意してほしい。

```Test.java
public class Test {
    public void hello(){
        System.out.println("こんにちは");
    };
}
```

```Sample.java
public class Sample {
    public static void main(String[] args) {

        //Testクラスを継承した匿名のサブクラスを定義し、同時にインスタンスを生成
        Test test = new Test() {
            @Override
            public void hello() {
                System.out.println("Hello");
            }
        }
        test.hello();
    }
}

```

## スーパークラスとインタフェースから利用できるメソッド
匿名クラスでは、インタフェースのデフォルトメソッド、
スーパークラスから引き継いだメソッドを利用することができる。

ここではスーパークラスのメソッドを利用した例を紹介する。


Testクラスというスーパークラスを継承した匿名クラスから
スーパークラスのメソッドを利用する場合、super を利用する。


```Test.java
public class Test {
    public void hello(){
        System.out.println("こんにちは");
    };
}
```

```Sample.java
public class Sample {
    public static void main(String[] args) {

        Test test = new Test() 
            //スーパークラスTestのhelloメソッドを呼び出す。
            super.hello();
        }
    }
}

```


以上が匿名クラスについての紹介である。