読者です 読者をやめる 読者になる 読者になる

せてぃーずノート

Javaのイベント参加レポートとかを書いたりします。

関数型インターフェースで気になったことを試してみた

関数型インターフェースとは?

ざっくり言うと、メソッドが1つだけ定義されたインターフェース。

関数型にならないケース

メソッド1つだけと言っても、defaultやstaticは対象外。
なので、defaultメソッドだけ持ったインターフェースは関数インターフェースと呼べない。

@FunctionalInterface
public interface A {
    default public int onlyMethod(int value) {
        return origin * 2;
    };
}

エラーメッセージはこんな感じ。

予期しない@FunctionalInterface注釈
Aは機能インタフェースではありません
  インタフェース Aで抽象メソッドが見つかりません

てか、関数型インターフェースって名前じゃないのか!
機械翻訳な気もするけど、Oracle公式は機能インターフェースなんですかねぇ。

関数型インターフェースと継承

関数型インターフェースを継承しても、メソッドが1つだけなら関数型インターフェースを名乗れる。
使いドコロは・・・ないと思うけど。

@FunctionalInterface
public interface A {
    int onlyMethod(int value);
}

@FunctionalInterface
public interface B extends A{
      // コンパイルエラーにならない。
}

当然ですけど、default修飾子付きのメソッドでオーバーライドするとコンパイルエラーです。
エラーメッセージは最初と同じで抽象メソッドが見つからないという内容です。

@FunctionalInterface
public interface C extends A{
    @Override
    default int onlyMethod(int value) {
        return 0;
    };
}

なお、@FunctionalInterfaceを外せばコンパイルは通ります。
それによって、インターフェースの抽象メソッドをインターフェースが実装するという謎なインターフェースが出来上がります。

継承(逆の場合)

関数型ではないインターフェースを継承して無理やり関数型にしてしまう。

public interface A {
   default int onlyMethod(int value) {
       return 0;
   };
}

@FunctionalInterface
public interface B extends A{
    int onlyMethod(int value);
}

デフォルトの実装を全く無視する荒業。
使い道あるかは知らぬ・・・。

多重継承

複数のインターフェースを継承した場合のパターンも試してみました。
まずはコンパイルエラーにならないパターン。

@FunctionalInterface
public interface A1 {
    int onlyMethod(int value);
}

@FunctionalInterface
public interface A2 {
    int onlyMethod(int value);
}

@FunctionalInterface
public interface B extends A1, A2 {
      // コンパイルエラーにならない。
}

当然、継承するインターフェースのメソッドの定義が違えばコンパイルエラーになります。

@FunctionalInterface
public interface A1 {
    int mainMethod(int value);
}

@FunctionalInterface
public interface A2 {
    int subMethod(int value);
}

@FunctionalInterface
public interface B extends A1, A2 {
      // これはコンパイルエラー。
}

ちなみにエラーメッセージはこんな感じ。

予期しない@FunctionalInterface注釈
Bは機能インタフェースではありません
インタフェース Bで複数のオーバーライドしない抽象メソッドが見つかりました

この場合、どちらかのメソッドを実装すればBを関数型インターフェースにすることができます。

@FunctionalInterface
public interface B extends A1 , A2 {
    @Override
    default int subMethod(int value) {
        return 1;
    };
}

やっぱり使いドコロが無さそうですが、あえて使うならこんな感じだと思います。

@FunctionalInterface
public interface C extends A , B{
    @Override
    default int subMethod(int value) {
        // デフォルトと言いつつ実装を強要してみる
        throw new UnsupportedOperationException();
    };
}

ここまでして関数型インターフェースを作りたい男の人って・・・。

まとめ

Lambdaに付随して言語仕様がガッツリ変わっているおかげで、インターフェース周りで色々出来て面白いです。
ただ、きちんと規約決めておかないとインターフェースに実装メソッドが大量発生したりstaticメソッドだらけになってカオスなものが出来上がる予感もします。。。