Java 10の主な変更点
Java 10では主に以下の変更が行われています。
- ローカル変数時のvarを用いた型推論
- Optionalクラス・コレクションクラスなどのAPI変更
- ガベージコレクションに対する改善とインターフェースの提供
- アプリケーションクラスも含めたクラスデータ共有(CDS)
- ルート証明書の提供(OpenJDK)
- Docker環境下での改善
- javahコマンドの廃止
- JavaベースのJITコンパイラ
- バージョン表記方法の変更
OpenJDKとOracleJDKの違いにより、細かい差異は他にも多少あります。ただし、Oracleからの無償版JDKの提供はこのJava 10で終了するので、Java 11以降、多くの開発者がOpenJDKを利用することになると思われます。
そのため、今までOracleJDKでは標準で提供されていた機能がOpenJDKに統合される流れになっています。例えば、ルート証明書の問題などはOpenJDKでよく生じる問題でしたが、それらが改善されています。
また、開発者にとって最も大きな変更点であり、注目しているポイントは「ローカル変数時のvarを用いた型推論」だと思います。今回はその変更点を中心に、その他のAPIの変更点も説明します。
ローカル変数時のvarを用いた型推論(1)
「型推論」というと少々堅苦しいイメージがありますが、簡単に言えば、変数利用時に型を意識せずにvarを使って型宣言ができる機能です。同様な機能はC#でもC++でもあったので、多くの方にとっては「Javaでもやっと使えるようになった」という印象だと思います。
また、基本的に型宣言をせずに利用できるJavaScriptやPHPといったスクリプト言語系を中心に使っている方であれば、Javaのコードは多少面倒と感じる部分もあったと思います。
型推論、もしくは暗黙的な型指定の機能は時にプログラマの勘違い等により危険なコードにもなりやすいため、スクリプト言語系ではTypeScriptなど、型宣言の機能強化を図る方向性もあるので、実際の利用では意見が分かれる部分ではあります。ただし、メリット・デメリットを理解して利用すれば、大変便利な機能です。
基本的な使い方
varで型宣言をする方法をリスト1に示します。
// (1)プリミティブな型の場合 var bl = true; // bool型 var chr = 'a'; // char型 var num1 = 10; // int型 var num2 = 10L; // long型 var num3 = 1.0; // double型 var num4 = 1.0F; // float型 // (2)オブジェクトの場合 var str = "Hello Java"; // String型 var list = new ArrayList<String>(); var fis = new FileInputStream(new File("/tmp/dummy.txt")); // (3)varでは記述できないもの List<String> list1 = new ArrayList<String>(); FileInputStream fis1 = null; // (4)varを使って(3)相当のことをやりたい場合の記述 var list2 = (List<String>)(new ArrayList<String>()); var fis2 = (FileInputStream)null;
プリミティブな型の基本的なものの場合には、(1)のようにそのまま値を右辺に設定します。ただし、数値型の場合にはリテラル表示を正しくしないと、正しい型にならないので注意が必要です。
わかっていてもうっかり間違ってしまいがちな部分であり、プリミティブな型に関してはそれほど面倒でもないので、型宣言をした方が確実かと思います
一方(2)のように、オブジェクト型に関しては、型宣言、そしてインスタンスの作成でも同じ記述をしなければいけませんでしたが、varを用いることで右辺だけ指定すればよくなります。
ただし、(3)のような記述を、varを使って記述することはできません。無理やり記述すれば、(4)のようにも記述できますが、あまりvarを使う必要性もなくなります。
varを使った型推論は、コンパイル時に型が右辺から特定できる場合に利用できる略式記述(シンタックス・シュガー)なので、この書き方をすることでこれまで実現できなかったことができるわけではありません。
また、初期値をともなったListやMapインターフェースを使った変数を定義したい場合には、リスト2のようにList/MapのofメソッドがJava 9から記述できます。
var list3 = List.of(1,2,3,4,5,6,7,8,9,10); var list4 = Map.of("key1","val2","key2","val2");
ただし、List/Mapのofメソッドにて作成されたオブジェクトは変更できません。
また、1行で記述されていて何をしているのか少々わかりにくいコードを分割してデバッグする際に、varを利用すると便利です。
例えば、リスト3の(1)のように何をしているのかわかりにくい1行コードがあり、それらを分割してデバッグ等を行いたい場合、それぞれの戻り値について型宣言をする必要がないため、import文も記述しなくてよくなります。
ただし、恒久的に記述したい場合には、右辺の戻り値の型がわかりにくくなるので、型宣言をした方がよいでしょう。
// (1) わかりにくい1行で記述されたコード例 var total = List.of(1,2,3,4,5,6,7,8,9,10).stream().mapToInt(num -> num).sum(); // (2) 何をしているのかを理解するために、デバッグしやすいように分割した例 var list = List.of(1,2,3,4,5,6,7,8,9,10); var st1 = list.stream(); var st2 = st1.mapToInt(x -> x); var total2 = st2.sum();