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については解説しませんでしたが、これはこれでおもしろいので、別エントリーでやるかもしれません。

2021/03/16

JEPでは語れないJava 16

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

半年ぶりのblog更新の季節がやってきました。春と秋はJavaのアップデートの季節です。

アメリカ西海岸時間の3月16日にJava SE 16がリリースされます。16日に16が出るわけですが、17は17日に出るのでしょうかw

Java 16は、LTSであるJava 17の1つ前のバージョンということがあり、いろいろと機能盛りだくさんです。IncubatorやPreviewのままJava 17に入れると、結局使えなくなってしまうので、そういったものはJava 16で入れておかなくてはいけないわけです。

Java 17で正式な機能にならないと、3年後になってしまいますから。

残念なのはswitch式でのパターンマッチが結局入らなかったことでしょうか。instanceofのパターンマッチは使えるのですが、本命はswitch式の方なので。

Java 16で取り入れられるJEPは16個。なかなか多いですね。

この中にはgitへの変更やGitHubへの移行、ビルド環境としてC++ 14の採用なども含まれているので、Javaの機能としては13個のJEPです。

また、Java 16ではAPIの変更が多いというのも特徴的です。

Project Panama関連で3つのJEP、それ以外にもUnixドメインソケットのJEPと、4つもAPIに関するJEPがあります。こんなにAPIに関するJEPがあったのは、JEPのプロセスになってからはじめてのような気がします。

JEPにはなっていないのですが、それ以外にも大きなAPIの変更があります。

ということで、ここ数年でAPIの変更が最も大きいバージョンになりそうです。

ただし、Panama関連のAPIの追加はIncubatorであり、規模も大きいので、このエントリーでは省略させていただきます。

また、いつものようにセキュリティ系のAPIはちゃんと理解していないので、省略します。

 

廃止になったAPI

Java 16で廃止されたAPIは1つだけです。

コンストラクタ

Java 14でforRemoval=trueになったコンストラクタが1つだけ削除されました。

  • javax.tools.ToolProvider()

ToolProviderクラスはコンパイラなどを取得するために使用するクラスですが、メソッドはすべてstaticメソッドで、インスタンスを生成する必要がないため、削除されました。

正式なAPIの廃止はこれだけなのですが、Preview機能に関するメソッド廃止というか変更もありました。それは、APIの追加のところで説明します。

 

廃止予定のAPI

Java 16では廃止予定APIの追加はJEP 390 Warnings for Value-Based Classesに関連するものが多いです。

JEP 390はプリミティブのラッパークラスなどのValue-Based Classのコンストラクタを廃止予定にし、使用時に警告を出すというものです。これはProject Valhallaに関連したJEPになります。

ちなみに、ラッパークラスのコンストラクタはすでに@Deprecatedだったのですが、foreRemovale=trueが追加されたということです。

メソッド

  • java.lang.ThreadGroup.destroy
  • java.lang.ThreadGroup.isDeamon
  • java.lang.ThreadGroup.isDestroyed
  • java.lang.ThreadGroup.setDaemon
  • java.lang.ThreadGroup.stop
  • java.awt.color.ICC_Profile.finalize
  • java.awt.image.ColorModel.finalize
  • java.awt.image.IndexColorModel.finalize

コンストラクタ

  • java.lang.Boolean(boolean value)
  • java.lang.Boolean(String s)
  • java.lang.Byte(byte value)
  • java.lang.Byte(String s)
  • java.lang.Character(char value)
  • java.lang.Short(short value)
  • java.lang.Short(String s)
  • java.lang.Integer(int value)
  • java.lang.Integer(String s)
  • java.lang.Long(long value)
  • java.lang.Long(String s)
  • java.lang.Float(float value)
  • java.lang.Float(String s)
  • java.lang.Double(double value)
  • java.lang.Double(String s)
  • java.net.URLDecoder()
  • javax.management.relation.RoleStatus()

ThreadGroupクラスはresumeメソッドやsuspendメソッドはすでにforRemovalがtrueになっていましたが、それに追加してdestroyメソッドなどが廃止予定に追加されています。

AWTの3つのメソッドはいずれもファイナライザーなので、使わない方がいいということは分かっていいので、標準ライブラリでも削除予定になっているわけです。

 

追加されたAPI

Java 15までは追加されたAPIがjava.baseモジュールとjava.compilerモジュールだけというのが続いていたのですが、Java 16ではこれ以外にjava.desktopモジュールなどでもAPIの追加が行われています。

また、Java 11以降で追加されたAPIはほとんどが言語仕様に関連したものが多く、一般の開発者が使うことがないようなAPIばかりでした。しかし、Java 16では久しぶりによく使いそうなメソッドが追加されています。

今回はその中でも重要度の高いStream APIなどについてはじめに紹介して、その後はいつも通りモジュール/パッケージのアルファベット順に紹介していきます。。

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

Stream APIでは、プリミティブ系のStreamに1つ、Streamインタフェースには2種類のメソッドが追加されました。また、これにともなって、3つの内部インタフェースが追加されています。

Streamインタフェース

Streamインタフェースでは2種類のメソッドが追加されたと書きましたが、4つは関連したメソッドで総数としては5つのメソッドが追加されました。

  • default <R> Stream<R> mapMulti(BiConsumer<? super T, ? super Consumer<R>> mapper)
  • default DoubleStream mapMultiToDouble(BiConsumer<? super T, ? super DoubleConsumer> mapper)
  • default IntStream mapMultiToInt(BiConsumer<? super T, ? super IntConsumer> mapper)
  • default LongStream mapMultiToLong(BiConsumer<? super T, ? super LongConsumer> mapper)
  • default List toList()

いずれもdefaultメソッドです。インタフェースへのメソッド追加なので、そうならざるをえないわけですね。

戻り値を見ていただければ分かる通り、mapMulti系が中間操作、toListメソッドが終端操作です。

mapMultiメソッドの引数はBiConsumerインタフェースですが、これは引数が2つ、戻り値なしの関数型インタフェースです。中間操作なのに、戻り値がないというのはどういうことなんでしょう。

どういう動作をするのかはソースを見てもらうのが手っ取り早いでしょう。

    default <R> Stream<R> mapMulti(BiConsumer<? super T, ? super Consumer<R>> mapper) {
        Objects.requireNonNull(mapper);

        return flatMap(e -> {
            SpinedBuffer<R> buffer = new SpinedBuffer<>();
            mapper.accept(e, buffer);
            return StreamSupport.stream(buffer.spliterator(), false);
        });
    }

mapMultiメソッドの引数のBiConsumerインタフェースは引数が2つですが、一方はストリームを流れてくる値、もう一方がConsumerオブジェクトです。

このConsumerオブジェクトが上記のソースだと変数bufferで表されるSpinedBufferオブジェクトです。このSpinedBufferクラスはBufferという名前がつくように何らかのバッファだと考えられます。そして、SpinedBufferクラスのacceptメソッドでバッファに要素を追加していきます。

そして、StreamSupportクラスのstreamメソッドでバッファの要素をストリーム化しているわけです。

ということで、BiConsumerインタフェースのラムダ式では、流れてきた要素に対し何らかの処理を行い、必要であれば第2引数のConsumerオブジェクトの顔をしたバッファに要素を追加していきます。

すると、次の処理にはConsumerオブジェクトに追加した要素が流れてくることになります。

たとえば、単純に条件に合致した要素だけを取り出す例で考えてみましょう。以下のコードは10以下の数だけを集める処理です。

    var numbers = List.of(0, 20, 10, 5, 50, 3);

    var result = numbers.stream()
            .mapMulti((num, consumer) -> {
                if (num < 10) consumer.accept(num);
            })
            .collect(Collectors.toList());
  

変数resultには0, 5, 3が入ります。

この例だとfilterメソッドを使ったのと変わりないですが、ストリームの要素がバラバラで統一的に処理できないような時に使うことができるはずです。

mapMulti系の他の3つのメソッドはいずれもプリミティブ系のストリームに変更する場合に使用するもので、使い方としてはmapMultiメソッドと同じです。

最後のtoListメソッドはストリームをリストに変換するメソッドです。単純ですが、使用機会はとても多いはずです。

実施的には、collectメソッドにCollectors.toList()を引数にした場合と同じ動作です。

これも実装を見てみましょう。

    default List<T> toList() {
        return (List<T>) Collections.unmodifiableList(new ArrayList<>(Arrays.asList(this.toArray())));
    }
  

Collectors.toListメソッドではなく、toArrayメソッドを使って変更不可能なリストを作成しています。

IntStream/LongStream/DoubleStreamインタフェース

プリミティブ系のストリームにもmapMultiメソッドが追加されています。

また、BiConsumerインタフェースに対応するように内部インタフェースがそれぞれ追加されています。

  • IntStream.IntMapMultiConsumer
  • LongStream.LongMapMultiConsumer
  • DoubleStream.DoubleMapMultiConsumer

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

まさかのjava.netパッケージにクラスが追加されましたよ。

これはJEP 380: Unix-Domain Socket Channelsに関連したクラスです。

UnixDomainSocketAddressクラス

Unixドメインソケットは同一PC内でのプロセス間通信を行う時に、高効率で通信できるソケットです。

JavaではTCP/IP通信と同じように扱うことができます。違いはアドレスを表すクラス、つまりこのUnixDomainSocketAddressクラスだけです。

UnixDomainSocketAddressクラスは、InetSocketAddressクラスと同様にSocketAddressクラスのサブクラスです。

Unixドメインソケットではファイルパスで指定して通信します。このため、UnixDomainSocketAddressクラスもインスタンシーエションはファイルパスで行います。

    var address1 = UnixDomainSocketAddress.of("/tmp/socketfile1");
    var address2 = UnixDomainSocketAddress.of(Path.of("tmp/socketfile2"));
  

ofメソッドは引数がStringクラスとPathインタフェースの2種類があります。

UnixDomainSocketAddressオブジェクトが生成できれば、後はTCP/IP通信と同様にSocketChannelなどで使用できます。

StandardProtocolFamily列挙型

Unixドメインソケットが追加されたので、StardardProtocolFamily列挙型にも定数が追加されました。

  • UNIX

通常は、この列挙型を使うことはほぼないはずです。

 

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

Buffer系のクラスにメソッドが追加されました。いずれも同じメソッドなのですが、引数の型がそれぞれ異なります。

ByteBuffer/CharBuffer/ShortBuffer/IntBuffer/LongBuffer/FloatBuffer/DoubleBufferクラス

いずれのクラスもputメソッドのオーバーロードが追加されています。以下にはByteBufferクラスの場合のシグネチャを示します。

  • ByteBuffer put(int index, ByteBuffer src, int offset, int length)

今までBuffer系のクラスにはput(int index, byte[] src, int offset, int length)メソッドとput(ByteBuffer src)メソッドはありました。しかし、書き込み位置と範囲を指定できるputメソッドがなかったのです。

まぁ、ByteBufferクラスだけを引数にして、適切なpositionとlimitを設定しておけば、同じ動作はできるんですけどね。分かりやすさを優先させたんでしょう。

 

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

ここからはそれほど使わないであろうAPIの変更をアルファベット順に説明していきます。

まずは、java.langパッケージですが、こちらはJEP関連のメソッド変更がありました。

Classクラス

Clsssクラスでは、JEP 360 Sealed Classes関連のAPI変更がありました。変更というのは名前とシグネチャが変わったということです。

  • Class[] getPermittedSubclasses()

getPermittedSubclassesメソッドが追加された代わりに、Java 15で導入されたpermittedSubclassesメソッドは廃止されました。

この2つのメソッドの違いは戻り値の型です。

permittedSubclassesメソッドはClassDesc[]だったのが、getPermittedSubclassesメソッドではClass[]になっています。

当然のことながら使いやすいのは、ClassDescクラスよりもClassクラスということだったのでしょう。

IndexOutOfBoundsException例外

コンストラクタが1つ追加されました。

  • IndexOutOfBoundsException(long index)

indexがintのコンストラクタはあったのですが、longはありませんでした。というか、配列のインデックスはintだけなので、配列を想定していたらlongは必要なかったわけです。

配列のインデックスがlongになることはないでしょうけど、Bufferとかはlongのインデックスがほしいと思うことはちょっとあります。さて、どうなるでしょうねw

 

 

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

indy/condy関連のパッケージですが、JEP 371 Hidden Classに関するAPIが追加されています。

MethodHandlesクラス

MethodHandlesクラスはIndyでメソッドをコールするために使用するMethodHandleクラスのユーティリティクラスです。そんなMethodHandlesクラスにメソッドが2つ追加されました。

  • static <T> T classData(MethodHandles.Lookup caller, String name, Class<T> type)
  • static <T> T classDataAt(MethodHandles.Lookup caller, String name, Class<T> type, int index)

どちらもIndyがはじめてコールされた時に使用されるブートストラップメソッドからコールされることを想定しています。簡単に言うと、引数のLookupオブジェクトから第3引数で指定されたクラスを返すというメソッドです。Hidden Classを作成した時を想定しています。まぁ、普通は使うことはないでしょうね。

MethodHandles.Lookupクラス

LookupクラスはindyやcondyでメソッドなどをルックアップするためのfindXXXメソッドを定義しています。

このLookupクラスにJava 15でdefineHiddenClassメソッドが追加されました。これに関連して、もう1つメソッドが追加されました。

他にラムダ式のように動的に生成したクラスを定義するためにdefineClassメソッドなどがあります。このdefineClassメソッドと同様なメソッドがHidden Class関連で追加されています。

  • MethodHandles.Lookup defineHiddenClass(byte[] bytes, Object classData, boolean initialize, MethodHandles.Lookup.ClassOption... options)

defineHiddenClassメソッドとの違いは、第2引数のclassDataです。これがクラスデータと呼ばれるものです。APIドキュメントにはpre-intialized class dataとなっています。

また、定数も1つ追加されました。

  • ORIGINAL

これはlookupModeで使用する定数ですが、他のMODULEとかPRIVATEなどの定数との違いはよく分かりません。

VarHandleクラス

VarHandleクラスは、MethodHandleクラスの変数版です。

このVarHandleクラスをinvokeできるかどうかに関するメソッドが3つ追加されました。

  • boolean hasInvokeExactBehavior()
  • VarHandle withInvokeBehavior()
  • VarHandle withInvokeExactBehavior()

VarHandleオブジェクトに対してinvokeを行うのはMethodHandleクラスですが、型を厳密に一致させる必要があるのがinvokeExactメソッド、必要に応じて引数や戻り値の変換もできるのがinvokeメソッドです。

invokeExact/invokeメソッドの引数に指定するのがVarHandleクラスですが、invokeExactメソッドを使用する時には厳密性があるかどうかがチェックされます。このチェックにhasInvokeExactBehaiviorメソッドが使われます。

そして、withInvokeBehaiviorメソッドとwithInvokeExactBehaiviorメソッドでinvocation時の振る舞いを変更することができます。

 

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

java.time.formatパッケージはDate and Time APIのフォーマッタに関するパッケージです。

DateTimeFormatterBuilderクラス

DateTimeFormatterBuilderクラスはDateTimeFormatterオブジェクトを生成するためのビルダークラスです。

そこに、朝、昼、夜などのDay Periodを表す文字列を追加するメソッドが追加されました。

  • DateTimeFormatterBuilder appendDayPeriodText(TextStyle style)

実際に試してみましょう。

jshell> var time = LocalTime.of(15, 0)
time ==> 15:00

jshell> var formatter = new DateTimeFormatterBuilder().
   ...> appendDayPeriodText(TextStyle.FULL).toFormatter()
formatter ==> DayPeriod(FULL)

jshell> System.out.println(formatter.format(time))
昼

というように15時だと、"昼"とフォーマットされました。ところが、FULLでもNARROWでもSHORTのどれでも"昼"になってしまうんですよね。英語だとすべてin the afternoonになってしまいます。

これはこれから変わるんでしょうか?

 

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

Stream APIが変更されたのだから、java.utilパッケージのコレクションに関連したのが追加されたのかと思ったのですが、空振りでしたw

Objectsクラス

Objectsクラスには範囲のチェックを行うcheckIndexメソッドやcheckFromIndexSizeメソッドがあるのですが、これに関連して3種類のオーバーロードが追加されました。

  • static long checkFromIndexSize(long fromIndex, long size, long length)
  • static long checkFromToIndex(long fromIndex, long toIndex, long length)
  • static long checkIndex(long index, long length)

違いは引数の型がintではなく、longになったということです。IndexOutOfBoundsException例外に関連しているんでしょうね。

 

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

毎度毎度のjavax.lang.modelパッケージです。

SourceVersion列挙型

Java 15に対応する定数が追加されました。

  • RELEASE_16

 

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

SwingにAPIの変更があるなんてビックリです。

JPasswordFieldクラス

パスワード用のテキストフィールドを表すJPasswordFieldクラスですが、スーパークラスのsetTextメソッドがオーバーライドされています。

  • void setText(String t)

なぜこのタイミングでこの変更が行われたのか、まったく分かりませんw

JSlider.AccessibleJSliderクラス

スライダーを表すJSliderクラス用のアクセシビリティ対応のクラスがAccessibleJSliderクラスですが、ChangeListenerインタフェースを実装しています。

そのChangeListenerインタフェースの定義しているメソッドがオーバーライドされているのですが、これ追加なのでしょうか? 単にAPIドキュメントに載っていなかったというだけのような気が...

  • void stateChanged(ChangeEvent e)

この他にもLook & Feelに関する部分でオーバーライドがいくつか追加されているのですが、省略します。

 

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

Logging APIにスレッド関連のAPIが追加されました。

LogRecordクラス

ログの1行に相当するLogRecordクラスにはスレッドのIDを記録できたのですが、これに関連して2つのメソッドが追加されました。

  • long getLongThreadID()
  • LogRecord setLongThreadID(long longThreadID)

これまで、スレッドのIDはintだったのですが、OSによってはlongのものがあるということでlongも扱えるようになったということのようです。Project FiberのVirtual Threadで大量のスレッドを扱えるようになるからかと思ったのですが、違っていたようですw

 

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

Java 11で導入されたHTTP Clientがjava.net.httpモジュールのjava.net.httpパッケージで定義されています。

HttpRequestクラス

HttpRequestオブジェクトを生成するためのビルダークラスがHttpRequest.Builderクラスです。このBuilderクラスのファクトリメソッドが1つ追加されました。

  • static HttpRequest.Builder newBuilder(HttpRequest request, BiPredicate<String, String> filter)

すでに存在するHttpRequestオブジェクトを再利用して、新しいHttpRequestオブジェクトを生成するためのビルダーのファクトリメソッドです。

第1引数が既存のHttpRequestオブジェクト、第2引数が第1引数のHttpRequestオブジェクトのヘッダーを残すかどうかを決めるフィルタになります。

BiPredicateインタフェースなので引数が2つ、戻り値の型がbooleanですが、第1引数がヘッダ名、第2引数にその値が指定されます。残すのであればture、残さないのであればflaseを返します。

HttpRequest.BodyPublishersクラス

POSTでHttpRequestオブジェクトを生成する時のボディを作成するのがBodyPublishersクラスです。

BodyPublishersクラスはstaticメソッドのファクトリメソッドだけが定義されているのですが、そのファクトリメソッドが1つ追加されました。

  • static HttpRequest.BodyPublisher concat(HttpRequest.BodyPublisher... publishers)

複数のBodyPublishersオブジェクトを連結させて、複数のボディを生成するBodyPublishersオブジェクトを生成するファクトリメソッドです。

 

ということで、一通りAPIの変更をさらってきましたが、LTSの1つ前のバージョンというのが如実に表れているような気がします。JEPで策定した以外の変更も多かったですね。

逆にJava 17は変更が少ないのかもしれません。少なくともIncubatorやPreviewは入れにくいでしょうし。

ということは、Java 16が使いこなせていれば、LTSのJava 17に移行するのも楽かもしれませんね。