実行時にアノテーション@Overrideが見つからなかった話

## これはなんですか
Java屋さん向けのjava.lang.Classやjava.lang.Methodやアノテーションを使ったあれやこれやする浅瀬くらいの深みな話。
ソース中でstream構文等を使用しているためjava9未満のバージョンをお使いの方はよくわからないかもしれませんが、心の目で見ればわかる、かも。

## やりたいこと

1. インターフェースに書いてあるメソッドをimplementsしたメソッドに @Override を書いてない輩を見つけたらあんじょうよろしゅうしたい
2. そうだ、単体テストで見つけよう！
3. 結論：だめでした

### 検証その１　指定したクラスのメソッド一覧を取得する

 単体テストのコード

``` Java
import java.util.Arrays;
import org.junit.jupiter.api.Test;

public class FindOverrideTest {

  @Test
  public void test() {
    Arrays.stream(Class1.class.getMethods())
    .forEach(method -> System.out.println(method.getName()));
  }

  interface Interface1 {

    void methodHasOverrideAnno();

    void methodHasNoOverrideAnno();
  }

  class Class1 implements Interface1 {

    @Override
    @Annotation1
    public void methodHasOverrideAnno() {

    }

    // @Override
    @Annotation1
    public void methodHasNoOverrideAnno() {

    }
  }
}
```

``` Java
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface Annotation1 {
}

```

実行すると得られる出力

```Text
methodHasOverrideAnno
methodHasNoOverrideAnno
wait
wait
wait
equals
toString
hashCode
getClass
notify
notifyAll
```

すべてのクラスは暗黙で `java.lang.Object` を継承しているため何も extends してないクラスでも wait, equals, toString等の Object のメソッドが見える

### 検証その２　`Class1` に記述されたメソッドを検出したい場合どうしたらいいか？

``` Java
  @Test
  public void test() {
    Arrays.stream(Class1.class.getMethods())
        .filter(method -> Class1.class.equals(method.getDeclaringClass())) // 後述のフィルタを追加 ※1
        .forEach(method -> System.out.println(method.getName()));
  }
```

method.getDeclaringClass() がそのメソッドが宣言されたクラスを返してくれるため、対象のクラスと同一であるかをチェックするフィルタを足します。

実行すると得られる出力

``` Text
methodHasOverrideAnno
methodHasNoOverrideAnno
```

### 検証その３ あのアノテーションをアレせよ

メソッドにくっついてるアノテーションを列挙してみます。

``` Java
  @Test
  public void test() {
    Arrays.stream(Class1.class.getMethods())
        .filter(method -> Class1.class.equals(method.getDeclaringClass()))
        .forEach(method -> {
          System.out.println(method.getName());
          System.out.println(" Annotations = " + Arrays.stream(method.getDeclaringAnnotations()) // 追加
              .map(annotation -> annotation.annotationType().getName()).collect(                 // 追加
              Collectors.joining(",")));                                                         // 追加
        });
  }
```

補足: 追加２行目の annotation.annotationType() で正しいクラスが取得できた

実行すると得られる出力

``` Text
methodHasOverrideAnno
 Annotations = Annotation1
methodHasNoOverrideAnno
 Annotations = Annotation1

```

あれ、おかしいな `java.lang.Override` がいない・・なぜだ。

・・・・・・

どうやら `java.lang.Override` は`@Retention(RetentionPolicy.SOURCE)` による宣言で、ソースファイルのみで保持されるものでした。※2

``` Java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
```

これを単体テストなどの実行時に検出するためには、RetentionPolicy は **Runtime** である必要がある。※2

本来ならこの処理はソースコードをチェックするSonarCubeとか、CodeGuruあたりでチェックすべきところだということがわかりました。

めでたしめでたし。

## あとがき　なぜOverrideを書いてない輩を誅したいか、について
openapi-code-generator(※3) でコントローラーのインターフェースを生成すると、defaultなメソッドが作成されるため、コンパイルが通ってしまい、インターフェースと実装されたクラスの乖離に気づかないことがあります。
openapi-code-generator側にはdefault宣言を抑止するフラグがなかったように思いますが、本当にそうだったかは要再確認。

このアプローチとは違うやり方ですが、実装されていないインターフェースのメソッドを発見したらあんじょうよろしゅうするという単体テストは記述できたので、結果としてよしとしてます。

## 参照
※1 https://stackoverflow.com/questions/2315445/how-to-quickly-determine-if-a-method-is-overridden-in-java 宣言されたクラス名と拾ったクラス名が一致しないと対象のクラスで実装されたことにならないということを教えてくれた記事　他に方法があれば教えて下さい

※2 https://itsakura.com/java-annotation-make アノテーションの説明です めったに使わないので `RetentionPolicy.SOURCE` の存在を失念していました。

※3 https://github.com/OpenAPITools/openapi-generator