※本記事は、Simon Roberts、Mikalai Zaikinによる”Quiz Yourself: Lambda Expressions (Advanced)“を翻訳したものです。
ラムダ式でvarを使用する際の細かい決まり
著者:Simon Roberts、Mikalai Zaikin
2020年2月27日
その他の設問はこちらから
過去にこのクイズの設問に挑戦したことがある方なら、どれ1つとして簡単な問題はないことをご存じでしょう。クイズの設問は、認定試験の中でも難しいものに合わせています。「中級者向け」「上級者向け」という指定は、設問の難易度ではなく、対応する試験による分類です。しかし、ほとんどすべての場合において、「上級者向け」の方が難しくなります。設問は認定試験対策として作成しており、認定試験と同じルールの適用を意図しています。文章は文字どおりに解釈してください。回答者を引っかけようとする設問ではなく、率直に言語の詳細な知識を試すものだと考えてください。
ラムダ式を作成し、ローカル変数とともに使用する際の知識を確認します。
ユーザー情報を含むPDFレポートを生成するソフトウェアを書いているとします。ドキュメント・ヘッダーのラベルの長さを計算するために、ユーザー名とタイトルを引数として受け取り、テキストの長さを返すラムダ式を作成します。このラムダ式は、次のように関数型インタフェースToIntBiFunctionを実装します。
ToIntBiFunction<Name, String> l = ... // この部分のコードは省略
あるフレームワークを使ってこの動作を呼び出し、メソッド引数のアノテーションにより、提供される引数の妥当性を保証します。たとえば、このフレームワークでは前述の機能を使用して、欠落した値(NULLポインタ)や範囲外の値などの問題を検知することができます。ラムダ式の実装では、最初のパラメータ(Name型のもの)にアノテーション@ValidNameを適用する必要があります。
以上の要件を満たすラムダ式はどれですか。2つ選んでください。
-
(@ValidName Name n, String t) -> n.firstName.length() + n.lastName.length() + t.length();
-
(@ValidName var n, String t) -> n.firstName.length() + n.lastName.length() + t.length();
-
(@ValidName var n, var t) -> n.firstName.length() + n.lastName.length() + t.length();
-
(@ValidName n, t) -> n.firstName.length() + n.lastName.length() + t.length();
-
(@ValidName Name n, var t) -> n.firstName.length() + n.lastName.length() + t.length();
解答:Java 10では、メソッドのローカル変数に適用できる予約済み型名として、varが導入されました。これを(指定された条件のもとで)使用して、ローカル変数を初期化する式から変数の型が推論されるようにすることができます。この提案は、JEP 286に記載されています。Java 11では、JEP 323によってこの機能が拡張されたため、varをラムダ式のパラメータとして使えるようになりました。
ラムダ式の引数の形式は、もともと、すべての引数に完全な型を指定するか、すべての引数の型を推論させるかのいずれかでした。型推論形式は、次のようになります。
(x) -> x.clear();
型明示形式は、次の例のようになります(型は非常に複雑な場合があることに注意してください。そのような、コードを理解するうえで型の明示的な知識があまり役に立たない状況は、型推論が便利な場面の1つです)。
(Map<String, List<Map.Entry<String, Long>>> x) -> x.clear();
型推論形式の場合、型宣言にアノテーションを追加することはできませんが、型明示形式では可能です(この場合、アノテーションはパラメータ自体ではなく、パラメータの型に適用されます)。そこで、@ValidMapアノテーションが存在すると仮定すれば、設問の例は次のようになるでしょう。
(@ValidMap Map<String, List<Map.Entry<String, Long>>> x) -> x.clear();
しかし、型推論形式ではアノテーションを適用できないため、次のコードは無効な構文でしょう。
(@ValidMap x) -> x.clear(); //
// 型がないためコンパイルできない
Java 11で導入されたJEP 323の拡張により、ラムダ式の引数で疑似タイプvarを使えるようになりました。この機能により、アノテーションに対応した簡潔な型推論構文を使用できます。アノテーションがない場合、コードは次のようになります。
(var x) -> x.clear();
これにアノテーションを適用することもできます。その場合、次のようになります。
(@ValidMap var x) -> x.clear();
この最後の形式を、元の形式と直接比較してみます。
(x) -> x.clear(); (@ValidMap var x) -> x.clear();
いずれの場合も、xの型は推論されることに注意してください。明示的な型を指定しないため、コードが簡潔になる可能性があるというメリットがあります。ただし、前者ではアノテーションの使用が認められていないのに対し、var疑似タイプを用いる後者ではアノテーションを使用できます。この構文には、アノテーションを付加する「型」の要素が含まれているからです。
ここまでで、アノテーションはvarまたは実際の型とともに使えますが、いずれも存在しない場合は使えないことがわかりました。選択肢Dは、型もvarもないところでアノテーションを使おうとしているため、選択肢Dは誤りであることがわかります。
もちろん、この設問はこれだけで解決できるものではありません。ラムダ式には、Java 8で導入されてから存在し続けている、別のルールがあります。そのルールとは、1つのラムダ式の引数リストで1つでも明示的に型を指定している場合、そのラムダ式のすべての引数で型を明示しなければならないというものです(逆に言えば、型を省略したい場合は、すべての型を省略しなければなりません)。
このルールは現在も適用されますが、varの使用に対応するために拡張されています。具体的には、すべての引数の型を明示する、すべての引数でvarを使って型推論を行う、すべての引数で明示的な型もvarも使わない、という3つから選択しなければなりません。
このルールから、1つのラムダ式の宣言で、ある引数にはvarを使い、その他の引数には明示的な型を使うというように混在させることはできないとわかります。そのため、選択肢BとEは誤りです。
また、1つのラムダ式の宣言において、すべての引数で同じ型メカニズムが使われている(つまり、すべてで型が明示されている)か、すべてでvarが使われているか、すべてに何も付けずに推論を行っている場合、(少なくとも前述のルールの点では)コードは正しいことになります。選択肢Aと選択肢Cは、これを満たしていることがわかります。選択肢Aではすべての引数で明示的な型が宣言されており、選択肢Cではすべての引数にvarが適用されています。そこから、選択肢Aと選択肢Cは正解であることがわかります。
この設問に関連した、役立つかもしれない補足を2つほどしておきます。1点目は、選択肢AはJava 8で有効だったのに対し、選択肢CはJava 11およびそれ以降でのみ有効であることです。2点目は、ラムダ式が型指定のない引数を1つ受け取る場合に限った特例があることです。次に例を示します。
(x) -> x.doSomething(); // OKラムダ式の通常の構文
この場合に限り、括弧を省略できます。そのため、次のコードも有効です。
x -> x.doSomething(); // OK単一引数ラムダ式の特殊な構文
ただし、このルールでは、以下の形式は認められていません。
SomeType x -> x.doSomething(); // 不可 -- 括弧が必要 var x -> x.doSomething(); // 不可 -- 括弧が必要
型を指定したい場合、またはvarを使用したい場合は、括弧を使用する必要があります。次に例を示します。
(SomeType x) -> x.doSomething(); // OK (var x) -> x.doSomething(); // OK
正解は選択肢AとCです。
![]() |
Simon RobertsSimon Roberts:Sun Microsystemsがイギリスで初めてJavaの研修を行う少し前にSunに入社し、Sun認定Javaプログラマー試験とSun認定Java開発者試験の作成に携わる。複数のJava認定ガイドを執筆し、現在はフリーランスでPearson InformITにおいて録画やライブによるビデオ・トレーニングを行っている(直接またはO’Reilly Safari Books Onlineサービス経由で視聴可能)。OracleのJava認定プロジェクトにも継続的に関わっている。 |
![]() |
Mikalai ZaikinMikalai Zaikin:ベラルーシのミンスクを拠点とするIBA IT ParkのリードJava開発者。OracleによるJava認定試験の作成に携わるとともに、複数のJava認定教科書のテクニカル・レビューを行っている。Kathy Sierra氏とBert Bates氏による有名な学習ガイド『Sun Certified Programmer for Java』では、3版にわたってテクニカル・レビューを務めた。 |


