Java 15の機能
6ヶ月サイクルの伝統に則り、2020年3月17日にJava 14がリリースされた後、次の非LTSバージョンであるJava 15が2020年9月15日に展開されます。
Java 15の新機能
以下はJava 15に含まれる機能の素早い概要です。
- Sealed Classes (Preview) – JEP 360
- Pattern Matching for instanceof (Second Preview) – JEP 375
- Records (Second Preview) – JEP 359
- Text Blocks (Standard) – JEP 378
- Hidden Classes – JEP 371
- Remove the Nashorn JavaScript Engine – JEP 372
- Reimplement the Legacy DatagramSocket API – JEP 373
- Disable and Deprecate Biased Locking – JEP 374
- Shenandoah: A Low-Pause-Time Garbage Collector – JEP 379
- Remove the Solaris and SPARC Ports – JEP 381
- Foreign-Memory Access API (Second Incubator) – JEP 383
- Deprecate RMI Activation for Removal – JEP 385
Mac OS上のJava 15インストール設定
- To get started with Java 15, download the JDK from here.
- Copy and extract the tar file in the /Library/Java/JavaVirtualMachines as shown below:
$ cd /Library/Java/JavaVirtualMachines
$ sudo cp ~/Downloads/openjdk-15_osx-x64_bin.tar.gz /Library/Java/JavaVirtualMachines
$ sudo tar xzf openjdk-15_osx-x64_bin.tar.gz
$ sudo rm openjdk-15_osx-x64_bin.tar.gz
それが完了したら、任意のテキストエディタを使用してbash_profileを開きます。私はvim ~/.bash_profileを使用しています。Java15のパスをJAVA_HOMEに設定し、変更内容を保存し、変更を反映するためにsource ~/.bash_profileを実行します。
export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-15.jdk/Contents/Home
最後に、Java 15を使用してプログラムをコンパイルして実行する準備が整いました。新しいJava 15の機能を迅速にテストするための対話型REPLコマンドラインツールであるJShellを使用します。
Java 15でリリースされた多くの機能はプレビュー段階であることに注意することが重要です。これは、現時点で完全に動作しているものの、将来的に変更される可能性があることを意味します。次のリリースサイクルでは、いくつかの機能が標準化されたり、単純に削除されたりするかもしれません。プレビュー機能をテストするためには、以下に示すようにJShellやJavaプログラムを実行する際に、明示的に–enable-previewを設定する必要があります。
jshell --enable-preview
javac --release 15 --enable-preview Author.java
次のいくつかのセクションでは、Java 15における重要な言語の変更について議論しましょう。
1. 封印されたクラス(プレビュー)
Kotlinでは、しばらく前から密封クラスが存在していましたが、Java 15ではついにこの機能が導入され、継承の制御がより向上しました。
Sealedクラスは、その名前が示す通り、特定のタイプにクラス階層を制限または許可することができます。
特定のクラスを切り替える必要性がある場合、これは非常にパターンマッチングに役立ちます。
以下の構文は、Java 15で封印されたクラスを定義します。
public sealed class Vehicle permits Car, Bike, Truck {
...
}
上記のコードは、キーワード「許可」の後に定義されたクラスのみがVehicleシールドクラスを拡張することができることを意味します。
もし、Car、Bike、TruckというクラスをVehicleと同じファイルに定義している場合、キーワードの”permits”は省略することができます。コンパイラがそれを暗黙的に処理します。以下のように示されています。
sealed class Vehicle {...}
final class Car extends Vehicle {...}
final class Bike extends Vehicle {...}
final class Truck extends Vehicle {...}
上記からわかるように、各クラスの最終修飾子を定義しました。これはシールドクラスの重要なルールであり、覚えておかなければならないことです:許可されたクラスは、明示的な修飾子(final、sealed、またはnon-sealed)で設定されている必要があります。
以下に、各修飾子が相続にどのような影響を与えるかを説明します。
- A permitted subclass that’s declared final cannot be extended further.
- A permitted subclass that’s declared sealed can be extended further but only by classes that are permitted by the subclass.
- A permitted subclass may be declared non-sealed can be extended further by any class. The superclass cannot restrict the subclasses further down this class hierarchy.
Java 15より前では、開発者は継承を制御するためにfinalキーワードかスコープ修飾子しか使用できませんでした。そのため、封印されたクラスは、クラスの階層を定義する際にJava開発者に追加の柔軟性をもたらします。
JavaのReflection APIには、シールドクラスを扱うための新しいメソッドが2つ追加されました。
java.lang.constant.ClassDesc[] getPermittedSubclasses();
boolean isSealed()
2. レコード(2番目のプレビュー)
Java 14で、レコードはプレビュー機能として導入され、POJOベースのデータキャリアクラスを記述する際の冗長なコードを削減するための試みがなされました。これは、データクラスの形で長い間Kotlinに存在していたものです。
Java 15では、レコードには2番目のプレビューが追加されました。大きな変更はなく、わずかな追加内容があるものの、いくつかの重要な明確化と制限事項がありますので、知っておく必要があります。
- Prior to Java 15, one could declare native methods in records(though it wasn’t a good idea). Now the JEP explicitly prohibits against declaring native methods in records. Understandably, defining a native method steals away the USP of records by bringing an external state dependency.
- The implicitly declared fields corresponding to the record components of a record class are final and should not be modified via reflection now as it will throw IllegalAccessException.
レコードはデータを保持するためのクラスですので、ネイティブメソッドの定義は避けるべきです。
封印されたタイプのレコード
私たちは、レコードが最終的で拡張できないことを知っています。しかし、幸いなことに、レコードはインターフェースを実装することができます。
次のように、封印されたインタフェースを定義し、それらをレコードに実装する方法があります。
sealed interface Car permits BMW, Audi { ... }
record BMW(int price) implements Car { ... }
record Audi(int price, String model) implements Car { ... }
地域の記録
メソッド内でも中間値を保存するためにレコードを定義することもできます。ローカルクラスとは異なり、ローカルレコードは暗黙的に静的とされます。これは、レコードが値のキャプチャを防ぐため、エンクロージングメソッドの変数やインスタンスメンバーにアクセスすることができないということを意味します。
ローカルレコードは、以前はヘルパーレコードを作成しなければならなかったJava開発者にとって、非常に便利なものです。
以下の方法で、ローカルレコードの導入がストリームAPIで値の計算を行うのをどのように助けるかを見てみましょう。
List<Merchant> findTopMerchants(List<Merchant> merchants, int month) {
// Local record
record MerchantSales(Merchant merchant, double sales) {}
return merchants.stream()
.map(merchant -> new MerchantSales(merchant, computeSales(merchant, month)))
.sorted((m1, m2) -> Double.compare(m2.sales(), m1.sales()))
.map(MerchantSales::merchant)
.collect(toList());
}
結論
Java 15では、上記の2つは主要な言語機能でしたが、ユーザーフィードバックのためにPattern Matchingの第2のプレビュー版もあり、Text Blocksは標準の機能となり、さらに重要なHidden classesの新機能もあります。
非公開クラスは、フレームワーク開発者に関連するJVMの機能です。これにより、Lookup::defineHiddenClassを使用してそれらを定義することで、クラスの実装を非表示にすることができます。これにより、Class.forNameやバイトコードで参照することでも、そのようなクラスを見つけることはできません。
Java 15で導入された主な変更は以下の通りです。