X

A blog about Oracle Technology Network Japan

クイズに挑戦:関数型インタフェース(上級者向け)

Guest Author

※本記事は、Simon Roberts、Mikalai Zaikinによる"Quiz Yourself: Functional Interfaces (Advanced)"を翻訳したものです。


想定通りに動作する関数型インタフェースの定義と実装

 

著者:Simon Roberts、Mikalai Zaikin

2020年2月27日

 

その他の設問はこちらから

過去にこのクイズの設問に挑戦したことがある方なら、どれ1つとして簡単な問題はないことをご存じでしょう。クイズの設問は、認定試験の中でも難しいものに合わせています。「中級者向け」「上級者向け」という指定は、設問の難易度ではなく、対応する試験による分類です。しかし、ほとんどすべての場合において、「上級者向け」の方が難しくなります。設問は認定試験対策として作成しており、認定試験と同じルールの適用を意図しています。文章は文字どおりに解釈してください。回答者を引っかけようとする設問ではなく、率直に言語の詳細な知識を試すものだと考えてください。

関数型インタフェースの書き方に関する知識を確認します。

次の4つのインタフェースについて:

interface Printable {
    void print();
}

interface Stringable {
    String toString();
}

interface Cloneable {
    Object clone();
}

interface InfoItem extends Printable, Stringable, Cloneable {}

Java 11で有効な関数型インタフェースはどれですか。2つ選んでください。

A.    Printable
B.    Stringable
C.    Cloneable
D.    InfoItem

解答:この設問は、関数型インタフェースが従うルールについて問うものです。コンパイルできるという単純な観点から見れば、上記のインタフェースはすべて有効です。つまり、それぞれの構文は有効なJavaインタフェースです。しかし、関数型インタフェースには追加の制約があります。この点は、Java 11のJava言語仕様のセクション9.8に記載されています。

このルールを簡単に説明する場合、「関数型インタフェースには厳密に1つの抽象メソッドがある」と言うのが一般的です。多くの場合、これは単一抽象メソッド・インタフェースまたはSAMインタフェースと呼ばれます。実は、この用語は他のいくつかの言語において中核的な仕様になっています。

以上のことを踏まえれば、選択肢A、B、Cは正しそうに見えます。しかし、選択肢Dは他の選択肢ほど正しいようには思えません。選択肢Dで定義されているインタフェースには、print()、toString()、clone()の各メソッドが必要になるでしょう。ただし、toString()とclone()というメソッドのシグネチャには見覚えがあるでしょう。この2つは、いずれもjava.lang.Objectクラスの中に存在しています。そこで定義されていることを考えれば、他のオブジェクトにも存在しているに違いありません。そこから、これらのメソッドは実のところ抽象メソッドではないと言うことができるでしょうか。

実は、Java仕様では追加の制限を定義することにより、この問題に対処しています。

関数型インタフェースの定義では、Objectのパブリック・メソッドでもあるインタフェースのメソッドは除外されます。

この点から、引数がなくStringを返す、設問のtoString()メソッドが、Objectのpublicメンバーとして定義されているtoString()メソッドとまったく同じであることは明らかなはずです。したがって、前述のルールを満たす抽象メソッドがStringableインタフェースには定義されていないことになるため、このインタフェースはJavaの有効な関数型インタフェースではありません。以上より、Stringableは関数型インタフェースではなく、選択肢Bは誤りであることがわかります。

それでは、Objectには引数のないclone()メソッドが定義されているため、選択肢Cも誤りということになるのでしょうか。そうではありません。ここで鍵となるのは、Objectのclone()メソッドがprotectedとして宣言されていることです。実際の定義は次のようになっています。

protected native Object clone() throws CloneNotSupportedException;

ここで問題になるのも、選択肢CのCloneableインタフェースで定義されているメソッドが、関数型インタフェースに必要な単一抽象メソッドの要件を満たすかどうかです。ルールは明確です。clone()メソッドがObjectに存在することは重要ではありません。clone()はObjectでpublicではないため、Cloneableは関数型インタフェースと見なされます。したがって、選択肢Cは正解です。

選択肢Aは簡単なはずです。print()メソッドは、このインタフェースで宣言されている唯一の抽象メソッド(実際には唯一のメソッド)であり、Objectには存在しません。そこから、選択肢Aも正解であることがわかります。

選択肢Dのインタフェースは少し異なっており、他の3つのインタフェースを拡張しています。インタフェースにabstractメソッドがある場合、そのメソッドを実装する義務が生じます。あるインタフェースが別のインタフェースを拡張する場合、新しいインタフェースはすべての拡張対象インタフェースにおけるすべての義務を引き継ぐことになります。ただし、そのインタフェースにいずれかの義務を満たすdefaultメソッドを追加される場合を除きます。したがって、次に例を示す3つのインタフェースは、いずれも1つの抽象メソッドdoIt()を宣言しているだけであるため、すべて有効な関数型インタフェースです。

@FunctionalInterface
interface A {
    void doIt();
}

@FunctionalInterface
interface B extends A {}

@FunctionalInterface
interface C extends B {}

これを踏まえれば、選択肢Dのインタフェースには、print()とclone()という2つのabstractメソッドが必要であることがわかります。設問のtoString()メソッドは、抽象メソッドであるという前述の条件を満たさないため、ここでの対象には含まれないことに注意してください。抽象メソッドが厳密に1つでなければならないというのは、関数型インタフェースのもっとも基本的な要件です。しかし、このインタフェースには2つのabstractメソッドがあるため、要件を満たしません。よって、InfoItemが関数型インタフェースであるはずがなく、選択肢Dは誤りです。

補足ですが、java.lang.Cloneableインタフェースは、設問のCloneableインタフェースとはまったく別物である点に注意してください。java.lang.Cloneableインタフェースにはメソッドが存在しないため、関数型インタフェースではありません。これはマーカー・インタフェースと呼ばれ、メソッドが存在しないインタフェースを指す用語として使われます。その一例がjava.io.Serializableです。マーカー・インタフェースは、クラスにラベルを付けることで、そのインタフェースを実装するインスタンスに関する決定を実行時に行うことができるようにする方法です。このアプローチは、Java 5で言語にアノテーションが導入される前に使われていました。Objectで宣言されているclone()メソッドは、サブクラスがjava.lang.Cloneableインタフェースを宣言しているかどうかにはよらず存在します。

正解は選択肢AとCです。
 


Simon Roberts

Simon Roberts:Sun Microsystemsがイギリスで初めてJavaの研修を行う少し前にSunに入社し、Sun認定Javaプログラマー試験とSun認定Java開発者試験の作成に携わる。複数のJava認定ガイドを執筆し、現在はフリーランスでPearson InformITにおいて録画やライブによるビデオ・トレーニングを行っている(直接またはO'Reilly Safari Books Onlineサービス経由で視聴可能)。OracleのJava認定プロジェクトにも継続的に関わっている。

 

Mikalai Zaikin

Mikalai Zaikin:ベラルーシのミンスクを拠点とするIBA IT ParkのリードJava開発者。OracleによるJava認定試験の作成に携わるとともに、複数のJava認定教科書のテクニカル・レビューを行っている。Kathy Sierra氏とBert Bates氏による有名な学習ガイド『Sun Certified Programmer for Java』では、3版にわたってテクニカル・レビューを務めた。

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.