2021/09/15

JEPでは語れないJava 17

このエントリーをはてなブックマークに追加

恒例の半年ぶりのblog更新です。

アメリカ西海岸時間の9月14日にJava SE 17がリリースされました。今回のJava 17は多くのディストリビュータがLTSにするバージョンなので、注目度も高いのではないでしょうか。

まぁ、常に新しいバージョンに移行している身にとってみては、LTSはあんまり関係ないんですけどねw

また、Oracle JDKが無償で使えるとか、LTSが2年周期になるとかいろいろ話題はあるようですが、ここでは触れないでおきます。

 

Java 17では17のJEPが導入されました。その中でPreviewがProject Amberによるパターンマッチング、IncubatorがProject Panama関連の2つがあります。

文法ではSealed ClassがPreviewが外されて、正式に導入されました。その他に注目すべきJEPとしてはMacのM1チップ用の移植があります。

その他は実装を改善したとか、Deprecatedを追加したとかが多いです。

 

廃止になったAPI

Java 15でDeprecatedになっていたRMI ActivationのAPIが削除されました。以下では、java.rmi.activationパッケージしか載せませんが、java.rmi.activationパッケージで定義されたクラスや例外も削除されています。

その他に、Java 16でforRemoval=trueになっていたURLDecoderのコンストラクタが削除されています。

 

パッケージ

  • java.rmi.activation

コンストラクタ

  • java.net.URLDecoder 

 

廃止予定のAPI

Java 17で追加された廃止予定のAPIには、JEP 398のApplet関連のAPIとJEP 411のセキュリティマネージャがあります。

セキュリティマネージャは、SecurityManagerクラス以外にも関連するクラスやメソッドが多くあるので、要注意です。とはいえ、SecurityManagerを使っている人を見たことがないので、そんな影響ないかなw

ちなみに、以下にはjava.appletパッケージのみ記載しますが、java.appletパッケージで定義されていたクラスも、もちろん廃止予定になります。

 

パッケージ

  • java.applet

クラス

  • java.bean.Thread.resume
  • java.beans.AppletInitializer
  • java.lang.SecurityManager
  • java.rmi.RMISecurityManager
  • java.security.AccessControlContext
  • java.security.AccessController
  • java.security.DomainCombiner
  • java.security.Policy
  • java.security.Policy.Parameters
  • java.security.PolicySpi
  • javax.security.auth.SubjectDomainCombiner
  • javax.swing.JApplet

例外

  • java.security.AccessControlException

メソッド

  • java.beans.Beans.instantiate
  • java.lang.SecurityManager.checkMulticast
  • java.lang.System.getSecurityManager
  • java.lang.System.setSecurityManager
  • java.lang.Thread.checkAccess
  • java.lang.ThreadGroup.checkAccess
  • java.util.concurrent.Executors.privilegedCallable
  • java.util.concurrent.Executors.privilegedCallableUsingCurrentClassLoader
  • java.util.concurrent.Executors.privilegedThreadFactory
  • java.util.logging.LogManager.checkAccess
  • javax.security.auth.Subject.doAsPrivileged(Subject,PrivilegedAction,AccessControlContext)
  • javax.security.auth.Subject.doAsPrivileged (Subject,PrivilegedExceptionAction,AccessControlContext)
  • javax.security.auth.Subject.getSubject
  • javax.swing.RepaintManager.addDirtyRegion

フィールド

  • javax.naming.Context.APPLET

 

追加されたAPI

なんだかよくわからないのですが、Java 17ではAPI大量に追加されてます。しかも、java.ioパッケージとかjava.util.concurrentパッケージなどよく使うパッケージでAPIが追加されています。

また、JEP 356で新しい乱数発生器が追加されています。なんと、java.util.randomというパッケージが追加されました。

 

java.base/java.ioパッケージ

java.ioパッケージではメソッドの追加が11もありますが、ほとんどがスーパークラスで定義されていたものをオーバライドしたメソッドです。たとえば、CharArrayReaderクラスのread(CharBuffer)メソッドや、FileInputStreamクラスのreadAllBytesメソッドなどがこれに相当します。

 

Consoleクラス

次のJava 18で導入予定のJEP 400で、入出力の文字コードのデフォルトがUTF-8で統一されるのですが、それに先駆けてなのかcharsetというメソッドが追加されました。

  • Charset charset()

このcharsetメソッドはConsoleクラスで使用している文字セットを返します。Windowsで実行するとWindows-31jが返り、Ubuntuで実行するとUTF-8が返ります。

ObjectInputFilterインタフェース

オブジェクトのデシリアライズをする時にチェックをするための使われていたObjectInputFilterインタフェースですが、staticファクトリメソッドが追加されました。

今までは、ObjectInputFiler.Configクラスにファクトリメソッドがあったのですが、それをインタフェース側に持ってきたような感じです。使い方はずいぶん違いますけど。

  • ObjectInputFilter allowFilter(Predicate<Class<?>> predicate, ObjectInputFilter.Status otherStatus)
  • ObjectInputFilter removeFilter(Predicate<Class<?>> predicate, ObjectInputFilter.Status otherStatus)
  • ObjectInputFilter merge(ObjectInputFilter filter, ObjectInputFilter anotherFilter)
  • rejectUndecidedClass(ObjectInputFilter filter)

allowFIlterメソッドは、条件に合致すれば許可を行うフィルターを作成するメソッドです。第2引数のStatus列挙型は第1引数のラムダ式がfalseを返した時に適用されます。

ちなみに、このファクトリで作成されたフィルタのcheckInputメソッドをコールされた時、引数のFilterInfoオブジェクトのserialClassメソッドの返り値がnullだとUNDEFINEDを返します。

removeFIlterメソッドはallowFilterメソッドの逆。

mergeメソッドはフィルタを統合するメソッドで、rejectUndecidedClassメソッドは引数のフィルタがUNDEFINEDを返すような場合にREJECTEDを返すようなフィルタを作成します。

ObjectInputFilter.Configクラス

ObjectInputFilterインタフェースにファクトリメソッドが追加されましたが、Configクラスにはファクトリメソッドのgetterとsetterメソッドが追加されました。システム全体のデフォルトのフィルタに使用されました。

  • static BinaryOperator<ObjectInputFIlter> getSerialFilterFactory()
  • static void setSerialFilterFactory(BinaryOperator<ObjectInputFilter> filterFactory)

 

java.base/java.langパッケージ

java.langパッケージでは変更のあったのは、Processクラスだけです。

Processクラス

ProcessクラスはProcessBuilderクラスで作成されたプロセスの制御を行うクラスです。

Java 17で追加されたのは起動されたプロセスの標準入出力に接続するためのメソッドです。

  • BufferedReader inputReader()
  • BufferedReader inputReader(Charset charset)
  • BufferedReader errorReader()
  • BufferedReader errorReader(Charset charset)
  • BufferedWriter outputWriter()
  • BufferedWriter outputWriter(Charset charset)

inputReaderメソッドがプロセスの標準出力を受け取るためのリーダーを取得するメソッドです。同様にerrorReaderメソッドがエラー出力、outputWriterメソッドが標準入力になります。

 

java.base/java.lang.invokeパッケージ

java.lang.invokeパッケージではラムダ式の実行で使われるAPIを定義しているので、普段使うことはほとんどないはず。

MethodHandlesクラス

MethodHandleクラス用のユーティリティクラスであるMethodHandlesクラスではメソッドが1つ追加されました。switch文で使われることを想定した、複数のMethodHandleオブジェクトを指定して実行する用途のMethodHandleオブジェクトを作成できます。

  • static MethodHandle tableSwitch(MethodHandle fallback, MethodHandle... targets)

まぁ、使うことはないかなw

 

java.base/java.lang.runtimeパッケージ

Java 16で追加されたラムダ式のブートストラップを定義するjava.lang.runtimeパッケージに、クラスが1つ追加されました。

SwitchBootstrapクラス

switch式でindyを使えるようにするためのブートストラップを提供するクラスです。ただしPreview機能です。

メソッドはブートストラップとなるtyepSwitchメソッドだけです。

  • static CallSite typeSwitch(MethodHandles.Lookup lookup, String invocationName, MethodType invocationType, Object... labels)

これも、使うことはないかなw

java.base/java.netパッケージ

UDPで通信を行うDatagramSocketクラスにメソッドが追加されました。

DatagramSocketクラス

マルチキャストのグループに関するメソッドが2つ追加されました。

  • void joinGroup(SocketAddress mcastaddr, NetworkInterface netIf) throws IOException
  • void leaveGroup(SocketAddress mcastaddr, NetworkInterface netIf) throws IOException

今までは、明示的にグループを扱えなかったので、マルチキャストはやりやすくなるはず。

 

java.base/java.nioパッケージ

MappedByteBufferクラスにメソッドが追加されました。というか、なんで今までなかったんだろう?

MappedByteBufferクラス

compactメソッド、duplicateメソッド、sliceメソッドが追加されていますが、いずれもByteBufferクラスのオーバーライドになので、説明は省略します。

 

java.base/java.timeパッケージ

Date & Time APIで使用されるjava.timeパッケージに新しいインタフェースが追加されました。

InstantSourceインタフェース

InstantSourceインタフェースで定義するメソッドは7つ。そのうちstaticファクトリメソッドが4種類、デフォルトメソッドが2種類。つまり、実装クラスで定義しなくてはいけないのメソッドは1つです。

  • Instant instant()
  • default long millis()
  • default Clock withZone(ZoneId zone)
  • static InstantSource fixed(Instant fixedInstatnt)
  • static InstantSource offset(InstantSource baseSource, Duration offsetDuration)
  • static InstantSource system()
  • static InstantSource tick(InstantSource baseSource, Duration tickDuration)

instantメソッドがInstantオブジェクトを返すメソッドで、これを実装する必要があります。

millisメソッドはSystem.currentTimeMillisメソッドと同等のメソッドです。エポック秒を返します。withZoneメソッドはInstantオブジェクトに基づいて、引数のタイムゾーン用のClockオブジェクトを返すメソッドです。

staticファクトリメソッドのfixedメソッドは常に同じ値を返すInstantオブジェクトを生成するInstantSourceオブジェクトを生成するためのメソッドです。まぁ、デバッグ用途ですかね。

systemメソッドはSystem.currentTimeMillisメソッドをソースとしたInstantSourceオブジェクトを返すメソッドです。

offsetメソッドと第2引数で指定された期間をオフセットとするためのファクトリメソッドです。

tickメソッドも引数は同じなのですが、動作はちょっと違います。第2引数で指定された期間は同じ値を返すInstantオブジェクトを生成するためのファクトリメソッドです。といっても、よく分からないと思うので、JShellで確かめてみます。

以下ではDurationとして2秒を指定しています。

jshell> var system = InstantSource.system()
system ==> SystemInstantSource

jshell> var duration = Duration.ofSeconds(2)
duration ==> PT2S

jshell> var instantSource = InstantSource.tick(system, duration)
instantSource ==> TickClock[SystemClock[Z],PT2S]

jshell> instantSource.instant().toEpochMilli()
$41 ==> 1631677098000

jshell> instantSource.instant().toEpochMilli()
$42 ==> 1631677100000

jshell> instantSource.instant().toEpochMilli()
$43 ==> 1631677102000

jshell> instantSource.instant().toEpochMilli()
$44 ==> 1631677102000

jshell> instantSource.instant().toEpochMilli()
$45 ==> 1631677104000

jshell> instantSource.instant().toEpochMilli()
$46 ==> 1631677104000

jshell> instantSource.instant().toEpochMilli()
$47 ==> 1631677106000

jshell> instantSource.instant().toEpochMilli()
$48 ==> 1631677106000

jshell> instantSource.instant().toEpochMilli()
$49 ==> 1631677108000

生成したInstantオブジェクトのtoEpochMillisメソッドは繰り返しコールしているのですが、奇数秒は返ってきません。偶数秒になっているのはたまたまですが、2秒ごとに時間が進んでいるのが分かるはずです。

 

java.base/java.utilパッケージ

java.utilパッケージには16進数用のフォーマッタクラスが追加されました。でも、なぜjava.textでなくてjava.utilなんだろう?

HexFormatクラス

HexFormatクラスは前述したように、16進数用のフォーマッタです。メソッドが36個もあるので、ここで全部を紹介すのはつらい...

of系のファクトリクラスが2種類で、通常はこれらを使用してHexFormatオブジェクトを生成します。また、with系のファクトリメソッドも5種類あります。

フォーマットを行うformatHexメソッドが4種類のオーバーロードがあります。これらは、バイト配列をフォーマットします。バイト配列ではなく、単一の数値であればtoHexDigitsメソッドを使用します。

逆に、文字列をバイト配列にフォーマットするには、parseHexメソッドを使用します。こちらも3種類のオーバーロードがあります。

また、staticメソッドであるfromHexDigitsメソッドもあります。こちらは文字か文字列をint型の値に変換します。

ちょっと使ってみましょう。

jshell> var format = HexFormat.of()
format ==> uppercase: false, delimiter: "", prefix: "", suffix: ""

jshell> format.toHexDigits(200)
$2 ==> "000000c8"

jshell> format.formatHex(new byte[]{0, 5, 10, 15, 20, 25})
$3 ==> "00050a0f1419"

jshell> var format2 = HexFormat.ofDelimiter(",")
format ==> uppercase: false, delimiter: ",", prefix: "", suffix: ""

jshell> format2.formatHex(new byte[]{ 0, 5, 10, 15, 20, 25})
$5 ==> "00,05,0a,0f,14,19"

ofメソッドで生成したHexFormatオブジェクトはバイト配列をフォーマットすると、すべて詰めて文字列にしてしまいます。カンマ区切りや空白区切りにしたい場合は、ofDelimiterメソッドを使用してHexFormatオブジェクトを生成します。

Map.Entryインタフェース

Map.Entryインタフェースにstaticメソッドのコピーメソッドが追加されました。

  • static <K, V> Map.Entry<K, V> copyOf(Map.Entry<? extends K, ? extends V> e)

コピーしたエントリーはどのマップとも結びついていない状態になります。

SplittableRandomクラス

SplitableRandomクラスは、新しく追加された乱数発生器のインタフェースを実装するように実装が変更されています。実装しているインタフェースは以下の3種類です。

  • RandomGenerator
  • RandomGenerator.SplittableGenerator
  • RandomGenerator.StreamableGenerator

また、以下のメソッドが追加されました。

  • SplittableRandom split(RandomGenerator.SplittableGenerator source)
  • Stream<SplittableRandom> splits()
  • Stream<SplittableRandom> splits(long streamSize)
  • Stream<SplittableRandom> splits(RandomGenerator.SplittableGenerator source)
  • Stream<SplittableRandom> splits(long streamSize, RandomGenerator.SplittableGenerator source)

splitメソッドやsplitsメソッドはストリーム処理の中で使われるので、一般には使わないと思います。

 

java.base/java.util.concurrentパッケージ

ThreadLocalRandomクラスにメソッドが追加されました。

ThreadLocalRandomクラス

nextFloatメソッドのオーバーロード2つが追加されています。いずれも、nextDoubleメソッドではできていた下限、上限値を設定できるようになっています。

  • float nextFloat(float bound)
  • float nextFloat(float origin, float bound)

boundが上限値(この値は含まない)、originが下限値(この値は含む)です。

 

java.base/java.util.randomパッケージ

JEP 356に基づいて、新しい乱数のパッケージができました。

詳細はいつか書きます。

 

java.compiler/javax.lang.modelパッケージ

SourceVersion列挙型

毎度のことですが、定数が追加されています。

  • RELEASE_17

 

java.compiler/javax.lang.model.utilパッケージ

javax.lang.model.utilパッケージではコンパイル時に使用するプログラム要素の扱うためのビジターなどをいろいろと定義しているパッケージです。

Elementsインタフェース

プログラムの要素に対する操作を行うインタフェースですが、Automatic Moduleのチェックメソッドが追加されました。

  • default boolean isAutomaticModule(ModuleElement module)

 

java.desktop/javax.swingパッケージ

まさかのSwingに変更がありました。ちょっとビックリw

JSlider.AccessibleJSliderクラス

Swingでスライダーを表すJSliderクラスのアクセシビリティサポート用のクラスがAccessbleJSliderクラスです。今まで実装していたインタフェースに加えてAccessibleActionインタフェースを実装するようになりました。

そのため、AccessibleActionインタフェースの3つのメソッドが追加されています。

  • boolean doAccessibleAction(int i)
  • int getAccessibleActionCount()
  • String getAccessibleActionDescription(int i)

他のSwingコンポーネントはAccessibleActionインタフェースを実装しているので、なぜ今になってJSliderクラスに加わったのかはちょっと謎。

 

java.desktop/javax.swing.filechooserパッケージ

ファイルチューザーが使用するファイルシステムのファサード的なクラスであるFileSystemViewクラスにメソッドが追加されています。

FileSystemViewクラス

ファイルチューザーで表示されるシステムアイコンを取得するためのメソッドgetSystemIconメソッドがオーバーロードされています。

  • Icon getSystemIcon(File f, int width, int height)

今までのgetSystemIconメソッドとの違いはアイコンのサイズを指定できるようになったことです。

 

これ以外にjava.xml.cryptoモジュールでRSA-PSSがサポートされました。

ということで、Java 17のAPIの変更をまとめました。

JEPに絡むところはjava.util.randomパッケージですね。それ以外にもちょこちょこと変更があります。とは言え、すぐに使えそうなクラスはHexFormatクラスぐらいでしょうか。

Project Panama関連のIncubator Moduleについては解説しませんでしたが、これはこれでおもしろいので、別エントリーでやるかもしれません。