ITお絵かき修行

3歩歩いても忘れないために

Upgrade Java SE 7 to Java SE 8 OCP Programmer(1Z0-810)対策 第一章 ラムダ式(2)

(1)に引き続き、ラムダ式の章を進める。

■注意
全文が意訳です。正確な情報は下記リンクを参照してください。

■試験対策ページ
java.boot.by

■試験
Upgrade Java SE 7 to Java SE 8 OCP Programmer | Oracle Certification Exam

1.2. 関数型インタフェースクラスの実装

関数型インタフェースクラスはラムダ式メソッド参照のための型を提供する。
各関数型インタフェースクラスは関数メソッドと呼ばれる単一の抽象メソッド(single abstract method (SAM))を持ち、ラムダ式のパラメータと戻り値に対応あるいは合致する。
JDK8において、既に存在するRunnabl、Callable、 Comparator、ActionListenerのような、単一のメソッドを持つインタフェースクラスは、現在の関数型インタフェースとラムダ式となる。これらは単一の抽象メソッドが使われているインタフェースクラスにて使用することができる。


@FunctionalInterface アノテーション
Java8では@FunctionalInterfaceという新しいアノテーションを関数型インタフェースに付与するよう紹介している。関数型インタフェースであるという意図の伝達とコンパイラにいくつかの追加のチェックを許可するためである。

※WARNING※
@FunctionalInterfaceはオプションである。Javaコンパイラに対するヒント句である。

例えば下記のインタフェースはコンパイルに成功する。

public interface MyInterface {
}

しかし関数型インタフェースであることを示した場合(コンパイルに失敗する)

// Java 8 compiler fails
@FunctionalInterface
public interface MyInterface {
}

抽象メソッドが無かった場合、コンパイラは"MyInterface is not a functional interface" や "no abstract method was found"といったエラーを発生させる。
2つ目のメソッドを追加した場合もエラーを発生させる。

// Java 8 compiler fails !
@FunctionalInterface
public interface MyInterface {
    void doIt();
    void doItNow();
}

インタフェースにおけるデフォルトメソッド
Java8のインタフェースクラスではデフォルトメソッドとstaticメソッドをサポートする。
デフォルトメソッドインスタンスメソッドの先頭に「default」キーワードを付けて定義する。またデフォルトメソッドはコードの本文を提供する。
デフォルトメソッドを含むインタフェースの具象クラスは、デフォルトメソッドとその他のメソッドをオーバーライドすることができる。
関数型インタフェースクラスは単一の抽象メソッドを必要とする。下記のコードはコンパイルに失敗する、無効な関数型インタフェースクラスである。

// Java 8 compiler fails !
@FunctionalInterface
public interface MyDefInterface {
    default void doIt() { /* cool implementation */ }
}


しかし下記の関数型インタフェースクラスはコンパイルに成功する。

@FunctionalInterface
public interface MyDefInterface {
    default void doIt() { /* cool implementation */ }
    void doItNow(); // Single Abstract Method (SAM)
}

インタフェースにおけるstaticメソッド
staticメソッドは定義したクラスと結びついている、というよりもクラスのどのオブジェクトでも使用できる。
各クラスのインスタンスは、クラス内でstaticメソッドを共有している。
Java8ではインタフェースに定義したstaticメソッドをデフォルトメソッドで補足することができる。

staticメソッドのようにインタフェースにstaticメソッドを定義したい時はメソッドシグネチャの先頭に「static」を付与する。
インタフェースクラスの全てのメソッド宣言は「public」となるので「public」キーワードは省略することができる。

staticメソッドを含むインタフェースクラスを実装したい時、staticメソッドはまだインタフェースクラスの一部であり、実装クラスの一部ではない。
理由はクラス名をメソッドの接頭辞に付加できないため。staticメソッドの呼び出し時はインタフェースクラス名を付記する必要がある。

関数型インタフェースは単一の抽象メソッドを定義する必要がある。下記のコードはコンパイルに失敗する、無効な関数型インタフェースである。

// Java 8 compiler fails !
@FunctionalInterface
public interface MyStatInterface {
    static void doIt() { /* cool implementation */ }
}

しかし下記の関数型インタフェースはコンパイルに成功する。

@FunctionalInterface
public interface MyStatInterface {
    static void doIt() { /* cool implementation */ }
    void doItNow();
}

java.lang.Objectクラスの関数型インタフェース
インタフェースクラスがjava.lang.Objectクラスの抽象メソッドのひとつをオーバーライドした場合、関数型インタフェースの抽象メソッドとしてカウントしない。
例えば java.util.Comparatorは関数型インタフェースであるが、2つの抽象メソッドを有している。

package java.util;

@FunctionalInterface
public interface Comparator<T> {

    int compare(T o1, T o2);

    boolean equals(Object obj);

    ...
}

(関数型インタフェースである)理由は、equals() メソッドjava.lang.Objectクラスの持つpublicメソッドシグネチャと一致するため。

【参考】
Java関数型インターフェースメモ(Hishidama's Java8 Functional Interface Memo)