Java 10 でのローカル型推論、またはアヒルのように鳴く場合
ごく最近、Oracle は新しい言語バージョンを 6 か月ごとにリリースするという新しい戦略を採用しました。この戦略では、3 バージョンごとに長期サポート (LTS) が提供されることを前提としています。それに関する簡単なメモ:
- LTS を備えた現在のバージョンは Java 8 です。
- Java 9 は 2018 年 3 月までしかサポートされないはずなので、すでに終了しています。
- Java 10 は 2018 年 9 月までサポートされます。
- LTS を持つ次のバージョンは Java 11 であると想定されています。これは 2018 年 9 月にリリースされる予定で、少なくとも 2023 年 9 月までサポートされる予定です。
詳細については、Oracle Java SE サポート ロードマップをご覧ください。
当社はまだ Java 9 を採用していませんが、Java 9 を完全にスキップして次の LTS にジャンプすることは有効な選択肢のようです。 Java 自体以外には、Spring Boot Java のバージョンなど、さらに多くの懸念事項があるため、おそらく慎重に検討する必要があります。それでも、避けられない変更を考慮して、Java 10 で導入されるものを調べることにしました。そして、そのバージョンで導入される主なものは、ローカル型推論のようです。
私たちは皆、この Java 構文を知っています:
List<User> list = new ArrayList<User>(); // or since Java 7 List<User> list = new ArrayList<>();
基本的に、ローカル型推論はそれを次のように置き換える可能性です:
// left side type is inferred from the right side and will be ArrayList var userList = new ArrayList();
これは、コードのボイラープレートが少し少ないことを意味しますが、変数とメソッドの名前にはもっと注意を払う必要があります。var キーワードは自明ではありません。
ローカル型推論は、Scala、C#、Go、そしてもちろん Kotlin など、多くのプログラミング言語で長い間使用されてきました。その点で Java は遅れをとっていましたが、今それを修正することにしました。ただし、これの具体的な実装に関しては、いくつかの論争がありました。たとえば、次の可能性がありました:
- 価値を持つ ローカル定数と var 用 Kotlin や Scala などの変数の場合
- 定数を持つ または単に最終 ローカル定数と var 用 const 以降の変数 そして最終 Java ではすでに予約されています。
- 最終変数を持つ 定数と var の場合 変数;
- let を利用する 、定義 または :=;
- 詳細はこちら
最後に、構文を既存のものに近づけ、var を許可することにしました。 ローカル変数と最終変数用 したがって、上記の例は Java 10 で機能します。ただし、置換は必ずしも簡単ではありません。 例:
// example 1 - list is a List<User> type List<User> list = new ArrayList<>(); // example 2 - userList is an ArrayList<Object> type, so you lose type information var userList = new ArrayList<>(); // example 3 - userListFixed is an ArrayList<User> type, so you keep type information var userListFixed = new ArrayList<User>();
例 2 では、左辺を直接置換すると、コンパイラはリスト型を推測できないため、デフォルトで Object になります。リストからアイテムを処理しようとすると、トリップします。
ローカル型推論が役立つ例の 1 つは、データ変換です。たとえば、あるタイプのオブジェクトを異なる属性を持つ別のタイプに変更し、そのために匿名クラスを使用したいとします。 Java 8 では、ストリーム スコープ内でしか実行できなかったため、たとえば、ストリーム パイプライン内では新しいプロパティにアクセスできますが、外部ではアクセスできませんでした。
List<User> users = Arrays.asList( new User("Elisabeth", "Bennett", 20), new User("Jane", "Bennett", 22), new User("Mary", "Bennett", 18), new User("Kitty", "Bennett", 17), new User("Lydia", "Bennett", 15) ); users.stream() .map(u -> new Object() { String fullName = u.firstName + " " + u.lastName; boolean canDrink = u.age >= 18; }) .forEach(u -> { if (u.canDrink) { System.out.println("+ " + u.fullName + " is of age and can drink"); } else { System.out.println("- " + u.fullName + " is not of age and cannot drink"); } }); /* Output will be * + Elisabeth Bennett is of age and can drink * + Jane Bennett is of age and can drink * + Mary Bennett is of age and can drink * - Kitty Bennett is not of age and cannot drink * - Lydia Bennett is not of age and cannot drink */
Java 10 では、新しいオブジェクトの変換されたリストをストリーム パイプラインの外で使用できました。
List<User> users = Arrays.asList( new User("Elisabeth", "Bennett", 20), new User("Jane", "Bennett", 22), new User("Mary", "Bennett", 18), new User("Kitty", "Bennett", 17), new User("Lydia", "Bennett", 15) ); final var objects = users.stream() .map(u -> new Object() { String fullName = u.firstName + " " + u.lastName; boolean canDrink = u.age >= 18; }) .collect(Collectors.toUnmodifiableList()); // do something with the users... System.out.println(); for (var o : objects) { if (o.canDrink) { System.out.println("+ " + o.fullName + " is of age and can drink"); } else { System.out.println("- " + o.fullName + " is not of age and cannot drink"); } }
そのため、ストリームの閉鎖が終わった後も、マップされたオブジェクト プロパティには引き続きアクセスできます。ただし、これはローカルの型推論であるため、現在のメソッドのローカル スコープ外でそれらを使用することはできません。 var キーワードはメソッド パラメーターの宣言では機能しないため、var を別のメソッドに渡すことはできません。したがって、var があるからといって、Java が魔法のように動的に型付けされるわけではありません。構文シュガーが少し追加されただけで、コンパイラが型を推測できる場所でのみ、静的に型付けされます。
私にとっては、Java が前進しようと努力しているにもかかわらず、その歴史的なルーツを維持しようとすることによって妨げられていることを示しています。下位互換性を維持し、イノベーションを最優先事項とはしていません。