X

A blog about Oracle Technology Network Japan

クイズに挑戦:コア関数型インタフェースの使用(中級者向け)

Guest Author

※本記事は、Simon RobertsとMikalai Zaikinによる"Quiz yourself: Using core functional interfaces (intermediate)"を翻訳したものです。


Predicate、Consumer、Function、Supplierの各インタフェースの使用方法に関する知識を確認する

著者:Simon Roberts、Mikalai Zaikin 
2020年6月8日 | 本記事をPDFでダウンロード
その他の設問はこちらから

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

このJava SE 11クイズでは、コア関数型インタフェースを使いたいと思います。

次のコード部分について:

public static void main(String[] args) throws InterruptedException,
                                              ExecutionException {
  var cf = CompletableFuture.supplyAsync(() -> "Java");
  cf = cf.thenCompose(s -> CompletableFuture.supplyAsync(() -> s + "11"));
  System.out.print(cf.get());
}

thenComposeメソッドに渡されるのは、どの関数型インタフェースのインスタンスですか。1つ選んでください。<\p>

A. java.lang.Runnable
B. java.util.concurrent.Callable
C. java.util.function.Function
D. java.util.function.BiConsumer
E. java.util.function.Supplier

 

解答:一目でわかるのは、この設問がCompletableFuture APIの知識を必要としているようだということです。しかし、実はそうではありません。試験では、その対象に含まれないはずの見慣れないAPIを含む設問を見かけることがあるかもしれません。見慣れないAPIが試験対象に含まれないことが正しいなら、そのAPIは実のところ設問の核心ではなく、その関連知識を使わずに回答できるという可能性が高いと言えます。今回の設問もそのような問題です。

ただし、Java 11の主な関数型インタフェースについての知識は、試験対象に含まれています。そして、この設問もその範囲内です。関数型インタフェースは多数ありますが、次の一般的な形態を知り、いくつかの変形に注意すれば、おそらく十分でしょう。

  • Supplier:引数を受け取らず、何かを返す
  • Consumer:1つ以上の引数を受け取り、voidを返す
  • Function:1つ以上の引数を受け取り、何かを返す
  • Predicate:1つ以上の引数を受け取り、プリミティブのbooleanを返す
  • 各種Operator(BinaryおよびUnary):引数と戻りタイプが同じでなければならないという制約がある
  • Bi接頭辞:引数が1つでなく2つであることを示す
  • Int、Long、Double、ToInt、ToLong、ToDoubleの各接頭辞:引数または戻りタイプがそのプリミティブであることを示す

java.util.functionパッケージで定義されている43個のインタフェースのうち、40個がこのルールの対象となります。

そして、おそらく知っておくべきインタフェースが、あと3つあります。

  • Runnable:引数を受け取らず、voidを返す
  • Callable:引数を受け取らず、ジェネリック型を返し、例外をスローする
  • Comparator:同じ型の2つの引数を受け取り、プリミティブのintを返す

それでは、thenComposeメソッドに何が渡されるかを確認してみます。

s -> CompletableFuture.supplyAsync(() -> s + "11")

設問のコードを確認するだけで、このラムダ式からいくつかのことがわかります。

  • ->の左辺は、1つの変数sです。そのため、このラムダ式は1つの引数を受け取ります。引数が括弧で囲われていないのは問題ありません。この1つの引数には、明示的な型もvar疑似タイプも指定されていないからです。
  • 本体は単純な式ですが、波括弧もreturnキーワードもありません。これらの要素は、いわゆる「式ラムダ」では省略可能です。
  • 返される型はvoidではありません。これは、前の行で同じメソッド呼出し(CompletableFuture.supplyAsync)が使われており、そこで変数cfに値を代入していることからわかります。

以上より、1つのパラメータを受け取るラムダ式があることがわかります。それでは、各選択肢について詳しく見ていきます。

選択肢AにはRunnableとありますが、Runnableのabstractメソッドは次の形式です。

public abstract void run();

つまり、パラメータは受け取らず、何も返しません。パラメータがないため、このラムダ式のターゲット型となることはできません。そのため、選択肢Aは誤りです。

選択肢BにはCallable、選択肢EはSupplierとあります。しかし、次に示すように、これらのインタフェースのabstractメソッドにはいずれも引数がありません。

Callableインタフェースは次の形式です。

public interface Callable<V> {
  V call() throws Exception;
}

そして、Supplierインタフェースは次の形式です。

public interface Supplier<T> {
  T get();
}

そのため、選択肢Bと選択肢Eはいずれも誤りです。

選択肢DにはBiConsumerとありますが、名前からわかるように、このインタフェースには2つの引数が必要です。

public interface BiConsumer<T, U> {
  void accept(T t, U u);
}

BiConsumerには2つの引数が必要であるため、選択肢Dは誤りです。

選択肢CにはFunctionとあります。このインタフェースでは、1つの引数を受け取る次のabstractメソッドを宣言しています。

public interface Function<T, U>  {
  U apply(T t);
}

このメソッドの引数の個数は要件に一致しているため、選択肢Cは正解です。そして、おそらくご想像のとおりと思いますが、このコードではコンソールにJava11と出力します。

ラムダ式が返す式から情報を集めることができるかどうかを考えてみるのも、興味深いことです。このラムダ式が実装しているメソッドは値を返さなければならないと考えて問題ないでしょうか。それとも、Consumerインタフェースのacceptメソッド(voidを返します)などをこのラムダ式が実装している可能性も考えられるでしょうか。示されているメソッドはvoidでない型を返すことはわかっていますが、Javaでは関数が返す値を無視することが認められています。そのことを示すとても簡単な例は、Listインタフェースのaddメソッドです。ほとんどの場合、addメソッドを使うときは戻り値を無視します。

List<String> ls = new ArrayList<>();
ls.add("Text");  // 返されるブール値を無視

しかし実際には、addメソッドはboolean値を返します。

Javaでは、CおよびC++に存在した、メソッド呼出しの戻り値を無視するという選択肢を採用しました。その理由は、この仕組みがとても便利だからです。Listのaddメソッドの場合、falseを返すことはほとんど考えられないため、戻り値を無視するのは一般的です。

戻り値が存在すること自体に疑問を感じる方もいるかもしれません。このaddメソッドは、Collectionのaddを実装しており、追加が成功したかどうかを示します。Setの場合、提供されるオブジェクトがSetにすでに含まれていた場合、addメソッドはfalseを返します。

しかし、戻り値を無視することが許可されている理由がどのようなものであるにせよ、この動作は有効です。そのため実際には、この設問に記述されているラムダ式をエラーなしでConsumerに代入することもできます(ただし、BiConsumerに代入することはできません)。

正解は選択肢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.