2022/03/22

JEPでは語れないJava 18

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

半年ぶりのJavaのアップデートの時間です。

Java 18はLTSの次のバージョンということもあり、JEPは少なめです。

  • 400: UTF-8 by Default
  • 408: Simple Web Server
  • 413: Code Snippets in Java API Documentation
  • 416: Reimplement Core Reflection with Method Handles
  • 417: Vector API (Third Incubator)
  • 418: Internet-Address Resolution SPI
  • 419: Foreign Function & Memory API (Second Incubator)
  • 420: Pattern Matching for switch (Second Preview)
  • 421: Deprecate Finalization for Removal

しかし、日本、特にWindowsの日本語環境で運用しているシステムにとって、とても重大な変更があります。

それが、JEP 400のUTF-8 by Defaultです。

Reader/Writer系のクラスで文字コードを指定しない場合、デフォルトの文字コードとしてUTF-8が使われるようになります。今まで、Windowsの日本語環境ではデフォルトの文字コードはWindows-31j (MS932)だったので、それを前提とした入出力は動作がおかしくなります。

もちろん、互換性維持のための策もちゃんとあって、file.encodingプロパティをCOMPAT、つまり実行時にオプションとして-Dfile.encoding=COMPATを指定すれば今まで通りのデフォルトの文字コードになります。

しかし、デフォルトの文字コードに依存したコードはなるべく早いうちに、文字コードを指定するコードに書き換えるべきです。

なお、NIO.2を使用した入出力はすでにデフォルトの文字コードがUTF-8になっているので、変更は必要ありません。

 

JEP 408は以前よりJDKに含まれていたWeb Server機能を使用できるツールの提供、JEP 413はJavadocにコードを書くために@snippetタグの導入です。

JEP 416はリフレクションの実装方法の改善です。JEP 421は以前より予告されていたfinalizerの廃止です。for Removalになったので、どんなに遅くなったとしても次のLTSではfinalizeメソッドは使えないと思った方がいいです。

他のIncubatorモジュールやPreviewは2ndや3rdなので、もうおなじみ?

 

例によってセキュリティ系のAPIの変更は櫻庭がよく理解していないので、省略します。

 

廃止になったAPI

メソッド

JEP 421関連でfinalizeメソッドが廃止になりました。これらはJava 16でfor Removalになっていたものです。

  • java.awt.color.ICC_Profile.finalize()
  • java.awt.image.ColorModel.finalize()
  • java.awt.image.IndexColorModel.finalize()

いずれもAWT関連なので、特に問題にはならないと思います。

 

廃止予定のAPI

Java 18で追加された廃止予定のAPIは、JEP 421関連のfinalizeメソッドがほとんどだけです。

 

メソッド

JEP 421関連のfinalizeメソッド以外のメソッドとして、とうとうThread.stop()メソッドがforRemovalになりました。stopメソッドを使われていることも少なくなっているとは思いますが、注意が必要です。

また、セキュリティ系の情報を保持するSubjectクラスのdoAsメソッドが廃止予定になりました。

  • java.awt.Graphics.finalize()
  • java.awt.PrintJob.finalize()
  • java.lang.Enum.finalize()
  • java.lang.Object.finalize()
  • java.lang.Runtime.runFinalization()
  • java.lang.System.runFinalization()
  • java.lang.Thread.stop()
  • java.util.concurrent.ThreadPoolExecutor.finalize()
  • javax.imageio.spi.ServiceRegistry.finalize()
  • javax.imageio.stream.FileCacheImageInputStream.finalize()
  • javax.imageio.stream.FileImageInputStream.finalize()
  • javax.imageio.stream.FileImageOutputStream.finalize()
  • javax.imageio.stream.ImageInputStreamImpl.finalize()
  • javax.imageio.stream.MemoryCacheImageInputStream.finalize()
  • javax.security.auth.Subject.doAs(Subject,PrivilegedAction)
  • javax.security.auth.Subject.doAs(Subject,PrivilegedExceptionAction)

 

追加されたAPI

LTSの次のバージョンということもあり、Java 18のAPIの追加はあまりありません。ただし、java.baseモジュールにメソッド追加が多いです。

 

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

java.ioパッケージでは2つのメソッドが追加されましたが、1つはスーパークラスで定義されていたものをオーバライドしたメソッドです。それがFileInputStreamクラスのtransferToメソッドです。

もう1つのメソッドがJEP 400に関連したメソッドです。

 

PrintStreamクラス

PrintSreamクラスでは現在使用している文字セットを返すcharsetメソッドが追加されました。

  • Charset charset()

Java 17でCosoleクラスにcharsetメソッドが追加されましたが、同じようにファイルに出力を行う文字セットを返します。

 

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

java.langパッケージではMathクラスとStrictMathクラスにメソッドが追加されました。いずれも同じメソッドなので、一緒に説明します。

Mathクラス/StrictMathクラス

Math/StrictMathクラスでは、それぞれ13のメソッドが追加されました。

  • int ceilDiv(int x, int y)
  • long ceilDiv(long x, int y)
  • long ceilDiv(long x, long y)
  • int ceilDivExact(int x, int y)
  • long ceilDivExact(long x, long y)
  • int ceilMod(int x, int y)
  • int ceilMod(long x, int y)
  • long ceilMod(long x, long y)
  • int divideExact(int x, int y)
  • long divideExact(long x, long y)
  • int floorDivExact(int x, int y)
  • long floorDivExact(long x, long y)
  • long unsignedMultiplyHigh(long x, long y)

Exactがつくメソッドは、ArithmeticException例外を投げるという違いだけなので、それ以外のメソッドを説明します。

ceilDiv系のメソッドは演算結果より大きいもしくは同じになる最小の整数を返します。

jshell> Math.ceilDiv(10,3)
$1 ==> 4

jshell> Math.ceilDiv(9, 3)
$2 ==> 3

jshell> Math.ceilDiv(-10, 3)
$3 ==> -3

ceilMod系の割り算の余りなのですが、負の値になることもあります。つまり、z = Math.ceil(x, y)だったときに、ceilModメソッドの返り値はx - z * yになります。

jshell> Math.ceilMod(10,3)
$1 ==> -2

jshell> Math.ceilMod(9, 3)
$2 ==> 0

jshell> Math.ceilMod(-10, 3)
$3 ==> -1

unsignedMultiplyHighメソッドはmultiplyHighメソッドの符号なし版です。

 

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

JEP 418でインターネットアドレスの名前解決がSPI化され、それに応じて4つのインタフェース/クラスが追加されました。

  • InetAddressResolverインタフェース
  • InetAddressResolver.LookupPolicyクラス
  • InetAddressResolverProviderインタフェース
  • InetAddressResolverProvider.Configurationクラス

これで独自の名前解決クラスを実装することができるようになります。

 

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

Charsetクラスにメソッドが1つ追加されています。

Charsetクラス

指定された文字セットを返すforNameメソッドがオーバーロードされました。

  • Charset forName(String charsetName, Charset fallback)

今までのforNameメソッドは指定された文字セットがない場合、IllegalCharsetNameException例外を投げます。これに対し、Java 18で追加されたforNameメソッドは文字セットがない場合、第2引数のfallbackが戻ります。

今までは文字セットがなかった場合は例外処理で処理しなくてはならなかったのが、fallbackを指定できるのでコードが分かりやすくなるはずです。

 

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

Durationクラスにメソッドが1つ追加されています。

Durationクラス

Durationクラスは時間間隔表すクラスですが、保持している時間間隔が正の値なのか調べるためのメソッドが追加されました。

  • boolean isPositive()

isZeroメソッドと、isNegativeメソッドはあったので、これで3つ揃いました。

 

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

HTTPリクエストを作成するBuilderクラスにメソッドが1つ追加されています。

HttpRequest.Builderクラス

HTTPリクエストを作成するBuilderクラスは、これまでHTTPメソッドのGET, POST, PUT, DELETEを作成するメソッドはあったのですが、HEADを作成するメソッドはありませんでした(HEADメソッドのリクエストを作ることはできます)。そこで、HEADメソッドが追加されています。

  • HttpRequest.Builder HEAD()

これまで、HEADメソッドはmethodメソッドで作らなくてはならなかったのが、少し簡単になりました。

 

java.xml/javax.xml.xpathパッケージ

なぜ、この時期にXPathにメソッドが追加されたのか全然わからないのですが、XPathのファクトリにメソッドが2つ追加されました。

XPathFactoryクラス

XPathFactoryクラスで属性を設定、取得するメソッドが追加されました。

  • String getProperty(String name)
  • void setProperty(String name, String value)

追加されたのはいいのですが、使い道がよく分かりません。デフォルトの実装ではUnsupportedOperationException例外が投げられるようになっています。

 

java.compilerモジュール

毎度のことですが、java.compilerモジュールはいろいろと追加されています。いつものソースバージョンやらなにやらが大半ですが、アノテーション処理関連でも少しだけメソッドが追加されています。とはいうものの、普通は使わないので省略します。

 

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

LTSの次のバージョンということで、変更点は少なめですね。

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に移行するのも楽かもしれませんね。

2020/09/09

JEPでは語れないJava SE 15

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

恒例になってしまった半年ぶりのblog更新ですw

アメリカ西海岸時間の9月15日にJava SE 15がリリースされます。今年は21日じゃないんですね。ちょっと残念。

Javaとは関係ないのですが、今年から9月21日はセプテンバーの日になってます。分からない人はEarth Wind & Fireでググってみてくださいw

APIに関しては、Java 14でIncubator Moduleとして提案されたJEP 383 Forein-Memory Access APIがSecond Incubatorになっています。うまくいけば、Java 16で正式になって、次のLTSに含まれそうです。

Project Panamaに関してはJEP 389 Forien Linker APIも提案されていて、これがJava 17に入るとJNI要らなくなるんですよね。すでにPanamaで提供されているjextractを使えばJNIはいらないですけど、JEP 389を使えばjextractも要らなくなるはず。

また、JEP 371 Hidden ClassesもAPIに関するJEPです。

これについては、後述します。

追加されるAPIもあれば、削除されるAPIもあります。

Java 15で削除されたのがJavaScriptエンジンのNashorn (JEP 372 Remove the Nashorn JavaScript Engine)。また、Deprecatedになったのが、RMI Activation (JEP 385 Deprecate RMI Activation for Removal)。さすがに、RMIは役目を終えていると思います。

 

JEPに関する変更はまた別の機会に書くとして (書けるかどうかはわからないですけど)、本エントリーはJEPになっていないものも含めてAPIの変更についてまとめます。

今回もABC順にならんでいます。同じように、セキュリティ系のAPIはちゃんと理解していないので、省略してます。

 

廃止になったAPI

Java 15ではNashornが廃止されましたが、Nashornはjdkではじまるモジュール(jdk.scripting.nashorn)なので、標準API的には変化がありません。

その他の廃止にAPIは定数が1つ、コンストラクタが2つととても少ないです。

フィールド

JMX Remote関連の定数が1つ削除されました。

  • javax.management.remote.rmi.RMIConnectorServer.CREDENTIAL_TYPES

CREDENTIAL_TYPESの代わりにCREDENTIALS_FILTER_PATTERNを使うようにします。とはいうものの、JMXをRMIで接続するコードを書くことは、自分で監視用ツールを作るということでもない限り使わないでしょうね。

コンストラクタ

Java 14でforRemoval=trueになったコンストラクタが早々と削除されました。

  • java.lang.invoke.ConstantBootstraps.<init>
  • java.lang.reflect.Modifier.<init>

ConstantBootstrapsクラスはCONDYのブートストラップのためのユーティリティクラスですが、もともとstaticなクラスメソッドしか定義されていないので、コンストラクタがある方がおかしかったわけです。

Modifierクラスもモディファイアの定数と、クラスメソッドのisXXXメソッド群を定義しているクラスなので、コンストラクタは必要ありません。基本的にはClass.getModifiersメソッドやMember.getModifiersメソッドで返されるint値を引数にして、isXXXメソッドでチェックするという使い方です。

このため、これらのコンストラクタが削除されたとしても、影響はないでしょう。

 

廃止予定のAPI

Java 15で追加された廃止予定APIは、前述したRMI Activationに関するものです。

パッケージ

  • java.rmi.activation

クラス/インタフェース

  • java.rmi.activation.Activatable
  • java.rmi.activation.ActivationDesc
  • java.rmi.activation.ActivationGroup
  • java.rmi.activation.ActivationGroup_Stub
  • java.rmi.activation.ActivationGroupDesc
  • java.rmi.activation.ActivationGroupID
  • java.rmi.activation.ActivationID
  • java.rmi.activation.ActivationInstantiator
  • java.rmi.activation.ActivationMonitor
  • java.rmi.activation.ActivationSystem
  • java.rmi.activation.Activator

例外

  • java.rmi.activation.ActivateFailedException
  • java.rmi.activation.ActivateException
  • java.rmi.activation.UnknownGroupException
  • java.rmi.activation.UnknownObjectException

 

追加されたAPI

Java 14で追加されたAPIのほとんどがjava.baseモジュールで、その他はjava.compilerモジュールだけです。

しかも、一番追加が多かったパッケージはjava.langパッケージ。とはいうものの、恒例のUnicodeのブロックとスクリプトがあるせいですけど。

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

java.langパッケージではクラス定義の変更や、前述したUnicode関連の変更、またJEP関連のメソッド追加があります。

Boolean/Byte/Character/Shortクラス

Java 12でCondyが導入されたため、新しく導入されたのがConstableインタフェースです。すでに、Java 12でIntegerクラスやDoubleクラスなどはConstableインタフェースを実装するように変更されていました。

Boolean/Byte/Character/Shortクラスはその時にもれていたプリミティブ型のラッパークラスで、Java 15でConstableインタフェースを実装するように変更されました。

Constableインタフェースの実装に伴い、以下のメソッドが追加されています。

  • describeConstable()

まぁ、普通の開発者は使わないでしょうけど。

CharSequenceインタフェース

Stringクラスでは定義されていたisEmptyメソッドが、CharSequenceインタフェースで定義されるようになりました。

  • boolean isEmpty()

isEmptyメソッドはデフォルトメソッドなので、CharSequenceインタフェースの実装クラスは変更なしです。また、その実装はStringクラスのそのままです。

Character.UnicodeBlockクラス

Java 14でサポートしていたUnicodeのバージョンは12.1ですが、Java 15ではUnicode 13をサポートするようになりました。

Unicode 13といえば、最近セブンイレブンでも売り出して話題のビャンビャン麺のビャンの文字が入るわけです。現時点で最も画数の多い漢字ですw

それはそうと、Unicodeのバージョンがあがると、当然のごとくブロックとスクリプトが追加されます。

追加されたブロックを示す定数は以下の8種類です。

  • CHORASMIAN
  • CJK_UNIFIELD_IDEOGRAPHS_EXTENSION_G
  • DIVES_AKURU
  • KHITAN_SMALL_SCRIPT
  • LISU_SUPPLEMENT
  • SYMBOLS_FOR_LEGACY_COMPUTING
  • TANGUT_SUPPLEMENT
  • YEZIDI

Character.UnicodeScript列挙型

スクリプトの定数は4種類追加されました。

  • CHORASMIAN
  • DIVES_AKURU
  • KHITAN_SMALL_SCRIPT
  • YEZIDI

Classクラス

Clsssクラスには2つのJEPに関連するメソッドが追加されました。

  • boolean isHidden()
  • boolean isSealed()
  • ClassDesc[] permittedSubclasses()

isHiddenメソッドがJEP 371 Hidden Classに関連したメソッドで、他の2メソッドがJEP 360 Sealed Classes関連のメソッドです。

isHiddenメソッドは、クラスがHidden Classかどうかを調べるメソッドです。Hidden Classについては、後述するMethodHandlesクラスの時に説明します。

ここでは、Sealed Classについて簡単に説明しておきましょう。

Sealed Classは継承を限定できるクラスです。

Sealed Classを定義するにはclassの前にsealedをつけ、派生させるクラスをpermitsの後に列挙します。

たとえば、Shpaeクラスを派生できるのはCircleクラスとRectangleクラスだけとしましょう。

C:>jshell --enable-preview
|  JShellへようこそ -- バージョン15
|  概要については、次を入力してください: /help intro

jshell> sealed class Shape permits Circle, Rectangle {}
|  次を作成しました: クラス Shape。しかし、 class Circle, and class Rectangleが宣言されるまで、参照できません

jshell> final class Circle extends Shape {}
|  次を作成しました: クラス Circle。しかし、 class Shapeが宣言されるまで、参照できません

jshell> final class Rectangle extends Shape {}
|  次を作成しました: クラス Rectangle

jshell> final class Triangle extends Shape {}
|  エラー:
|  クラスはシール・クラスShapeを拡張できません
|  final class Triangle extends Shape {}
|  ^-----------------------------------^

jshell>

ShapeクラスはCircleクラスとRectangleクラスのみ派生できるように定義したので、それ以外のTriangleクラスを派生させようとしてもコンパイルエラーになります。

なぜ、こんな機能が必要かというと、今後導入されるswitch式でのパターンマッチングなどに使えるからです。派生クラスが制限されていれば、default句が必要なくなるからです。

ちなみに、CircleクラスやRectangleクラスがfinal classなのは、Circleクラスをさらに派生させたクラスを作成できてしまうとsealする意味がなくなってしまうからです。

ただし、final classではなく、多重にsealed classにすることはできます。

さて、ここまでくれば、isSealedメソッドがSealed Classかどうかを調べるクラスだということはすぐにわかりますね。

jshell> Shape.class.isSealed()
$4 ==> true

jshell> Circle.class.isSealed()
$5 ==> false

jshell>

ShapeクラスはisSealedメソッドでtrueが返りますが、Circleクラスはfalseになります。

もう1つのpermittedSubclassesメソッドはSelaed Classを派生できるクラスの一覧を返すメソッドです。

jshell> Shape.class.permittedSubclasses()
$6 ==> ClassDesc[2] { ClassDesc[Circle], ClassDesc[Rectangle] }

jshell>

Sealed Classではない普通のクラスでpermittedSublassesメソッドをコールすると、空の配列が帰ります。

まぁ、Sealed Classを使うことはあっても、普通はpermittedSubclassesメソッドを使わないでしょうけど。

Mathクラス/StrickMathクラス

2つのクラスとも同じメソッドが追加されました。

  • static int absExact(int a)
  • static long absExact(long a)

どちらも実装は同じです。

普通のabsと何が違うかというと、引数がMIN_VALUEの時にArithmeticException例外がスローされるというところです。

 

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

Java 12で追加されたcondy関連のパッケージですが、このパッケージのクラス群もよっぽどのことがないかぎり使わないと思います。とりあえず、追加されたところだけさらっと流します。

ConstantDescsクラス

ConstantDescsクラスはプリミティブ型などの定数に対するXXXDescを示す定数が定義されているクラスです。そこに、4種類の定数が追加されました。

  • DirectMethodHandleDesc BSM_EXPLICIT_CAST
  • DirectMethodHandleDesc BSM_GET_STATIC_FINAL
  • DynamicConstantDesc<Boolean> FALSE
  • DynamicConstantDesc<Boolean> TRUE

 

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

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

ConstantBootstrapsクラス

削除されたAPIのところで、コンストラクタが削除されたと書いたConstantBootstrapsクラスです。

ConstantBootstrapsクラスだけはHidden Classとは関係ないメソッドが追加されています。ConstantDescsクラスで追加された定数に対応するメソッドです。

  • Object explicitCast(MethodHandles.Lookup lookup, String name, Class<?> dstType, Object value)

ようするにキャストをcondyで行う時のブートストラップです。これはMethodHandleクラスを使えば記述できるのですが、それを簡単に書けるようにしただけのメソッドです。

MethodHandles.Lookupクラス

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

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

  • MethodHandles.Lookup defineHiddenClass(byte[] bytes, boolean initialize, MethodHandles.Lookup.ClassOption... options)
  • Class<?> ensureInitialized(Class<?> targetClass)

defineClassメソッドのHidden Class版がdefineHiddenClassメソッドです。

defineClassメソッドでロードされたクラスは普通のクラスと同様に扱えますが、Hidden Classはその名の通り隠されたクラスなので普通のクラスとはいくつかの違いがあります。

たとえば、匿名クラスと違って名前があるとか、リフレクションでしかアクセスできないなどです。また、初期化は第2引数がtrueの場合にしか行いません。

後から初期化を強制的に行うようにするのが、ensureInitialziedメソッドです。返り値が初期化を行ったClassオブジェクトになります。

とはいうものの、これらのメソッドを使うことはないだろうなぁ...

MethodHandles.Lookup.ClassOption列挙型

LookupクラスのdefineHiddenClassメソッドで使用するenumが新規追加されました。

定義している定数は次の2種類です。

  • NESTMATE
  • STRONG

NESTMATEはネストしたクラスとしてHidden Classを扱うために使います。STRONGはクラスローダーと強い関係を持たせる場合です。強い関係というのは普通のクラスと同様にクラスローダーがGCされた時に、Hidden Classもアンロードされるということらしいです。

 

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

java.lang.reflectパッケージはリフレクションに関する情報を扱うために使用します。

AnnotatedTypeインタフェース

AnnotatedTypeインタフェースはアノテーションで修飾された型の情報を扱うためのインタフェースですが、メソッドが3つ追加されました。

  • <T extedns Annotation> T getAnnotation(Class<T> annotationClass)
  • Annotation[] getAnnotations()
  • Annotation[] getDeclaredAnnotation()

これらのメソッドはデフォルトメソッドではなくて、普通のメソッドです。そんなことしたら、実装クラスが... と思うかもしれませんが、まったく問題ありません。というのも、これらのメソッドはAnnotatedTypeインタフェースの親インタフェースのAnnotatedElementインタフェースで定義されているメソッドだからです。

それを、なぜ今さらAnnotatedTypeインタフェースに定義しなおしたんですかねぇ。よくわかりません 🤔

 

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

java.langパッケージのサブパッケージは普通の開発者は、まず触れないパッケージでしたけど、ここからは使うかもしれないパッケージです。

CharBufferクラス

Bufferクラスのサブクラスはいろいろありますが、その中のCharBufferクラスだけメソッドが追加されました。

  • boolean isEmpty()

StringクラスのisEmptyメソッドはlengthが0かどうかをチェックしますが、CharBufferクラスはちょっと違います。remainingが0かどうかをチェックします。

Bufferクラスのremainingはpositionとlimitの間にある要素数なので、読み込みを行った後、残りが空かどうかを調べるという使い方になります。

jshell> import java.nio.*

jshell> var buffer = CharBuffer.allocate(10)
buffer ==>

jshell> buffer.isEmpty()
$3 ==> false

jshell> buffer.append("ABC")
$4 ==>

jshell> buffer.isEmpty()
$5 ==> false

jshell> buffer.position(0)
$6 ==> ABC

jshell> buffer.isEmpty()
$7 ==> false

jshell> buffer.position(10)
$8 ==>

jshell> buffer.isEmpty()
$9 ==> true 

jshell>

CharBufferオブジェクトをアロケートしただけで、中身は空だとしても$3のようにisEmptyメソッドはfalseを返します。この時、positionは0、limitは10だからです。

結局、最後に示したようにpositionを10に進めると、isEmptyメソッドがtrueを返します。ほんとに文字があるかどうかではないので、注意が必要です。

 

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

NIOのチャネルもメソッドが追加されています。

SocketChannelクラス

ソケットチャネルにオープンのメソッドが追加でオーバーロードされました。

  • static SocketChannel open(ProtocolFamily family)

引数の型のjava.net.ProtocolFamilyインタフェースはプロトコルのファミリーを表すわけですが、このままだとなんだかよくわかりません。実際に使用するのはProtocolFamilyインタフェースを実装したStandardProtocolFamily列挙型です。

このenumではINETとINET6という定数が定義されています。ようするに、IPv4かIPv6かということです。

今までSocketChannelクラスでは、使用されているIPアドレスによってIPv4とIPv6を自動的に使い分けていたのですが、新たにオーバーロードされたopenメソッドはそれを明示的に行うということです。

 

ServerSocketChannelクラス

ServerSocketChannelクラスもopenメソッドがオーバーロードされています。

  • static ServerSocketChannel open(ProtocolFamily family)

返り値の型が違うだけで、SocketChannelクラスと一緒ですね。

 

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

java.nio.channels.spiパッケージはNIOのチャネルのプロバイダを定義しているパッケージですが、ここでもProtocolFamilyインタフェースに関連するメソッドが追加されました。

SelectorProviderクラス

通信を多重化して扱うSelectableChannelクラスのプロバイダがSelectorProviderクラスです。追加されたのは、SocketChannel/ServerSocketChannelクラスで追加されたopenメソッドと同様のメソッドです。

  • SocketChannel openSocketChannel(ProtocolFamily family)
  • ServerSocketChannel openServerSocketChannel(ProtocolFamily family)

 

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

java.utilパッケージに何が追加されたのかと思うかもしれませんが、肩透かしですw

NoSuchElementException例外

なんで今さら、コンストラクタが追加されたのかとても謎なのですが...

  • NoSuchElementException(String s, Throwable cause)
  • NoSuchElementException(Throwable cause)

 

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

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

SourceVersion列挙型

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

  • RELEASE_15

 

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

java.compilerモジュールなので、普通の開発者はまず使わないとは思いますが...

Elementインタフェース

javax.lang.reflectパッケージのAnnotatedTypeインタフェースが変更されたのと同じく、Elementインタフェースもアノテーション関連のメソッドが追加されました。

  • <A extends Annotation> A[] getAnnotationsByType(Class<A> annotationType)

 

Modifier列挙型

クラスなどを修飾する要素を定数として定義するModifier列挙型に、JEP 360 Sealed Classesに関する定数が2つ追加されました。

  • NON_SEALED
  • SEALED

 

TypeElementインタフェース

TypeElementインタフェースはクラスもしくはインタフェースに関する要素を取得するために使用するインタフェースです。ここでも、JEP 360 Sealed Classesに関連してSealed Classで派生することが許されているクラスの一覧を取得するメソッドが追加されています。

  • List<? extends TypeMirror> getPermittedSubclasses()

 

ここで示した機能追加以外にセキュリティ関連でメソッドが追加されていることと、Project PanamaのForeign-Memory Access APIが追加されています。

Forein-Memory Access APIはSecond Incubatorで、何も問題がなければJava 16で正式なAPIになると思います。Forein-Memory Accessもそうですが、Forein-Function Interface関連はいろいろとおもしろいので、機会があれば取り上げてみようと思います。

それにしても、Java 15で追加された機能のうち、普通の開発者が使いそうなAPIはSealed Class関連とjava.nio.CharBufferクラスのメソッド追加ぐらいなのがちょっと寂しいですね。

2020/03/17

JEPでは語れないJava SE 14

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

Javaのアップデートでしか更新しないようになってしまいましたが、半年ぶりのblog更新ですw

ということで、アメリカ西海岸時間の3月17日にJava SE 14がリリースされます。

今回もJEPに含まれていないAPIの変更をメインにJava 14についてまとめていきます。

とはいうものの、Java 14は16もJEPがあります。特にProject Amberがらみの文法変更や、ZGCがWindowsとOS Xに対応したことなどが注目されてますね。

そして、久しぶりにIncubator ModuleがJEPで提案されました!!

Project Panamaで策定されているForeign Funtion Interfaceに関連するモジュールで、ヒープではないメモリへのアクセスを行うためのAPIです。

JEPに関する変更はまた別の機会に書くとして、本エントリーはJEP以外のAPIの変更についてです。

今回もABC順にならんでいます。同じように、セキュリティ系のAPIはちゃんと理解していないので、省略してます。

 

廃止になったAPI

Java 14ではPack200関連の3つのクラスが廃止されました。

これらは、Java 11で@DeprecatedのforRemovalがtrueになったクラスです。

  • java.util.jar.Pack200
  • java.util.jar.Pack200.Packer
  • java.util.jar.Pack200.Unpacker

クラス3つといっても、実際にはPack200クラスの内部クラスも含んでいます。Pack200はもう役目を終えましたね。

もう1つ、セキュリティのACL (Access Control List)関連のパッケージが廃止されました。

こちらは、Java 10でforRemovalがtrueになったものです。

  • java.security.acl

java.security.aclパッケージに含まれていたインタフェース/例外は以下の通り。

  • java.security.acl.Acl
  • java.security.acl.AclEntry
  • java.security.acl.Group
  • java.security.acl.Owner
  • java.security.acl.Permission
  • java.security.acl.AclNotFoundException
  • java.security.acl.LastOwnerException
  • java.security.acl.NotOwnerException

これらのAPIはjava.security.Policyで置き換えができるはずです。

 

廃止予定のAPI

Java 14で追加された廃止予定APIは以下の通り。

メソッド

  • java.lang.Thread.resume
  • java.lang.Thread.suspend
  • java.lang.ThreadGroup.allowThreadSuspension
  • java.lang.ThreadGroup.resume
  • java.lang.ThreadGroup.suspend

コンストラクタ

  • java.lang.invoke.ConstantBootstraps
  • java.lang.reflect.Modifier
  • javax.tools.ToolProvider 

ThreadクラスとThreadGroupクラスのresumeメソッド、suspendメソッドは使うのははばかられていたので、廃止されてくれた方が分かりやすくていいですね。

ConstantBootstrapsクラスはCONDYのブートストラップのためのユーティリティクラスですが、もともとstaticなクラスメソッドしか定義されていないので、コンストラクタがある方がおかしかったわけです。

Modifierクラスもモディファイアの定数と、クラスメソッドのisXXXメソッド群を定義しているクラスなので、コンストラクタは必要ありません。基本的にはClass.getModifiersメソッドやMember.getModifiersメソッドで返されるint値を引数にして、isXXXメソッドでチェックするという使い方です。

ToolProviderクラスも同じくクラスメソッドしか定義していないので、コンストラクタがあるのがおかしかったわけです。

 

追加されたAPI

Java 14で追加されたAPIはjava.baseモジュール、java.compilerモジュール、java.xmlモジュールの3つです。とはいうものの、ほとんどがjava.baseモジュールです。

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

java.ioパッケージではメソッドの追加が2、アノテーションの追加が1つありました。

PrintStreamクラス

なぜ今になって追加されたのか全然理解できませんが、2つのメソッドが追加されました。

  • write(byte[] buf)
  • writeBytes(byte[] buf)

いずれも、内部ではthis.write(buf, 0, buf.length)をコールしています。唯一の違いは、writeメソッドがIOException例外をスローするのに対し、writeBytesメソッドは例外をスローしないという点です。

Serialアノテーション

古のJavaではオブジェクトをシリアライズ/デシリアライズする時にSerializableインタフェースを実装する必要がありました。しかし、Serializableインタフェースはマーカーインタフェースでメソッドは定義されていません。もし、シリアライズ/デシリアライズする時に特別な処理が必要な場合はwriteObject/readObjectメソッドなどを定義する必要があります。

@Serialアノテーションはそれを補うため、シリアライズ/デシリアライズに関連したメソッド/定数を修飾するために使われます。

@Serialアノテーションを使用すべきメソッドは下記の5種類です。

  • private void writeObject(java.io.ObjectOutputStream stream) throws IOException
  • private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException
  • private void readObjectNoData() throws ObjectStreamException
  • ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException
  • ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException

定数は次の2種類。

  • private static final ObjectStreamField[] serialPersistentFields
  • private static final long serialVersionUID

Javadocにはここであげたメソッド/定数以外に@Serialアノテーションを使うことはセマンティックエラーだと書いてあるのですが、今のところコンパイル時にチェックすることはやっていないような気が... 今後のバージョンではコンパイル時のチェックに使われるのでしょうか?

 

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

Java 14では新しいRecords型がプレビュー機能で導入されますが、それに伴ってAPIにも変更がありました。

Classクラス

後述しますが、Records型をリフレクションで扱うためのクラスが導入されており、Classクラスはそれに対応したメソッドが追加されています。

  • RecordComponent[] getRecordComponents()
  • boolean isRecord()

Records型とはいっていますが、実際にはちょっと特殊なクラスというだけなので、Classクラスで扱うことができます。isRecordメソッドはクラスがRecords型かどうかをチェックするためのメソッドですね。

もう1つのgetRecordComponentsメソッドはRecords型で定義されたフィールドを取り出すためのメソッドです。名前から分かると思いますが、RecordComponentクラスがRecords型で定義されたフィールドの情報を保持するクラスです。

jshell> record Data(int x, int y) {}
|  次を作成しました: レコード Data

jshell> var clzz = Data.class
clzz ==> class Data

jshell> var comps = clzz.getRecordComponents()
comps ==> RecordComponent[2] { int x, int y }

jshell> Stream.of(comps).forEach(System.out::println)
int x
int y

jshell>

Records型のDataはint xとint yを定義しているので、getRecordComponentsメソッドの返り値は要素が2つの配列になります。

そのまま出力してみると、フィールドの型と名前が表示されました。

RecordComponentクラスに関しては後ほどもうちょっと説明を加えます。

 

Recordクラス

ClassクラスのところでRecords型はちょっと特殊なクラスと言いましたが、Records型のスーパークラスとなるのがRecordクラスです。列挙型のスーパークラスがEnumクラスだというのと同じようなものですね。

たとえば、Records型のDataで試してみます。

C:\sample>cat Data.java
public record Data(int x, int y) {}

C:\sample>javac --enable-preview --release 14 Data.java
注意:Data.javaはプレビュー言語機能を使用します。
注意:詳細は、-Xlint:previewオプションを指定して再コンパイルしてください。

C:\sample>

Records型はまだプレビュー機能なので、コンパイルに--enable-previewオプションが必要です。また、Java 14以前では使えないので、--release 14を指定しておく必要があります。

これでData.classファイルが生成されたので、javapしてみましょう。

C:\sample>C:\sample>javap -p Data
Compiled from "Data.java"
public final class Data extends java.lang.Record {
  private final int x;
  private final int y;
  public Data(int, int);
  public java.lang.String toString();
  public final int hashCode();
  public final boolean equals(java.lang.Object);
  public int x();
  public int y();
}

C:\sample>

DataクラスがRecordクラスを派生させたクラスであることが分かります。また、xとyがフィールドとして宣言され、アクセッサ―メソッドも生成されています。

後は、おなじみのコンストラクタ、equalメソッド、hashCodeメソッド、toStringメソッドが作られていることも分かります。

ついでなので、もうちょっと見てみましょう。

C:\sample>javap -p -c Data
Compiled from "Data.java"
public final class Data extends java.lang.Record {
  private final int x;

  private final int y;

  public Data(int, int);
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Record."<init>":()V
       4: aload_0
       5: iload_1
       6: putfield      #7                  // Field x:I
       9: aload_0
      10: iload_2
      11: putfield      #13                 // Field y:I
      14: return

  public java.lang.String toString();
    Code:
       0: aload_0
       1: invokedynamic #18,  0             // InvokeDynamic #0:toString:(LData;)Ljava/lang/String;
       6: areturn

  public final int hashCode();
    Code:
       0: aload_0
       1: invokedynamic #22,  0             // InvokeDynamic #0:hashCode:(LData;)I
       6: ireturn

  public final boolean equals(java.lang.Object);
    Code:
       0: aload_0
       1: aload_1
       2: invokedynamic #26,  0             // InvokeDynamic #0:equals:(LData;Ljava/lang/Object;)Z
       7: ireturn

  public int x();
    Code:
       0: aload_0
       1: getfield      #16                 // Field x:I
       4: ireturn

  public int y();
    Code:
       0: aload_0
       1: getfield      #17                 // Field y:I
       4: ireturn
}

C:\sample>

バイトコードを眺めていると、コンストラクタとアクセッサ―メソッドはフィールドに対してのアクセスを行っているだけということが分かります。

おもしろいのが、equalsメソッドなどがINDYで実装されていることです。後述しますが、これに関連したクラスが追加されています。

ちなみに、Recordクラスを派生させたクラスを自作することはできません。

jshell> class Data extends Record {}
|  エラー:
|  クラスは直接java.lang.Recordを拡張できません
|  class Data extends Record {}
|  ^--------------------------^

jshell>

 

StrictMathクラス

StrictMathクラスには6つのメソッドが追加されました。

  • static int decrementExact(int a)
  • static long decrementExact(long a)
  • static int incrementExact(int a)
  • static long incrementExact(long a)
  • static int negateExact(int a)
  • static long negateExact(long a)

decrementExtractメソッドは引数の値を1減算するためのメソッド、incrementExactメソッドは1加算、negateExactメソッドが符号の反転を行うメソッドです。もし、int/longをオーバーフローする場合はArithmeticException例外をスローします。

jshell> int x = 0
x ==> 0

jshell> StrictMath.decrementExact(x)
$10 ==> -1

jshell> int y = Integer.MIN_VALUE
y ==> -2147483648

jshell> StrictMath.decrementExact(y)
|  例外java.lang.ArithmeticException: integer overflow
|        at Math.decrementExact (Math.java:1006)
|        at StrictMath.decrementExact (StrictMath.java:879)
|        at (#12:1)

jshell>

内部的にはMathクラスのdecrementExactメソッドをコールしているだけです。逆にいうと、Mathクラスに定義してあるのにStrictMathクラスにはなかったメソッドを追加したという感じでしょうか。

NullPointerException例外

これまでThrowable例外で定義されていたメソッドがオーバーロードされました。

  • String getMessage()

「なぜ今になってgetMessageメソッドが」と思うかもしれません。その理由はJEP 358 Helpful NullPointerExceptionsにあります。試してみれば、今までよりは分かりやすいメッセージになっているはずですよ。

 

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

ElementType列挙型

Records型が導入されたので、ElementType列挙型の定数も追加されています。

  • RECORD_COMPONENT

ElementType列挙型はTargetアノテーションの引数に使用します。Targetアノテーションはアノテーションを自作する時に使い、自作するアノテーションが何を修飾するものなのかを示します。

Records型のコンポーネントを修飾するアノテーションを作るときに、この定数を使うわけですね。

 

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

MethodHandles.Lookupクラス

普通に使う人には、どう考えても使わないクラスだと思いますが...

INDYで動的にコールするメソッドを定義する時に使用されるのがMethodHandleクラスです。そのMethodHandleオブジェクトをルックアップする時に使われるのが、MethodHandles.Lookupクラスです。ラムダ式などで使われています。

このLookupクラスに追加されたのが、以下の2メソッドです。
  • boolean hasFullPrivilegeAccess()
  • Class<?> previousLookupClass()

Lookupオブジェクトにはアクセス権が設定されています。この中で、privateアクセスされるかどうかをチェックするのがhasPrivateAccessメソッドです。

このhasPrivateAccessメソッドはJava 14でDeprecatedになりました(forRemovalは設定されていません)。

その代わりに追加されたのが、hasFullPrivilegeAccessメソッドです。

hasFullPrivilegeAccessメソッドはprivateアクセスとmoduleアクセスされるかどうかをチェックするメソッドです。

一方のhasPrivateAccessメソッドも動作が変更になり、privateアクセスとmoduleアクセスの両方をチェックするメソッドになりました(内部ではhasFullPrivilegeAccessメソッドをコールしています)。

さて、もう1つの方のpreviousLookupClassメソッドですが、Java 14でLookupクラスはクロスモジュールルックアップがサポートされたことに関連しています。

クロスモジュールルックアップは複数のモジュールに分かれている時に使われます。Javadocに記載されている例を見てもらうのが一番分かりやすいでしょうか。

    Lookup lookup = MethodHandles.lookup();   // in class C
    Lookup lookup2 = lookup.in(D.class);
    MethodHandle mh = lookup2.findStatic(E.class, "m", MT);

このlookup2変数が呼び出し元のlookup変数を返すために使われるのがpreviousLookupClassメソッドです。まぁ、そんなもんかと思っていただければいいかなw

 

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

RecordComponentクラス

前述したようにRecords型の導入に伴ってリフレクションでRecords型を扱うためのクラスがRecordComponentクラスです。Records型自身はClassクラスで扱えるので、Records型で定義するコンポーネントにリフレクションでアクセスするためのクラスです。

RecordComponentクラスは、他のリフレクション用のクラスと同様にAnnotatedElementインタフェースの実装クラスです。

定義されているメソッドは以下の通り。

  • Method getAccessor()
  • AnnotatedType getAnnotatedType()
  • <T extends Annotation> T getAnnotation(Class<T> annotationClass)
  • Annotation[] getAnnotations()
  • Annotation[] getDeclaredAnnotations()
  • Class<?> getDeclaringRecord()
  • String getGenericSignature()
  • Type getGenericType()
  • String getName()
  • Class<?> getType()
  • String toString()

アノテーションに関するメソッドはAnnotatedElementインタフェースで定義されているメソッドです。

getNameメソッドやgetTypeメソッドはその名のとおりですね。おもしろいのが、自動生成されるアクセッサ―メソッドを取得するgetAccessorメソッドや、コンポーネントを定義しているRecords型を取得するgetDeclaringRecordメソッドが定義されているところなどですね。

getGenericSignatureメソッドはジェネリクス化されたコンポーネントの場合、そのシグネチャを文字列で返します。ジェネリクス化されていないとnullが返るようです。

getGenericTypeメソッドはコンポーネントの型を返します。ジェネリクスなコンポーネントであれば型パラメータも一緒に取得できます。ジェネリクスでなければgetTypeと同じ結果になります。

jshell> record Data(String text) {}
|  次を作成しました: レコード Data

jshell> var clz = Data.class
clz ==> class Data

jshell> var component = clz.getRecordComponents()[0]
component ==> java.lang.String text

jshell> System.out.println(component.getDeclaringRecord())
class REPL.$JShell$11$Data

jshell> System.out.println(component.getName())
text

jshell> System.out.println(component.getType())
class java.lang.String

jshell> System.out.println(component.getGenericSignature())
null

jshell> System.out.println(component.getGenericType())
class java.lang.String

jshell> record Data2(List<String> texts) {}
|  次を作成しました: レコード Data2

jshell> var clz2 = Data2.class
clz2 ==> class Data2

jshell> var comp2 = clz2.getRecordComponents()[0]
comp2 ==> java.util.List texts

jshell> System.out.println(comp2.getGenericSignature())
Ljava/util/List<Ljava/lang/String;>;

jshell> System.out.println(comp2.getGenericType())
java.util.List<java.lang.String>

jshell> System.out.println(comp2.getType())
interface java.util.List

jshell>

Stringクラスのコンポーネントの場合、getGenericSignatureメソッドはnullを返しています。また、getGenericTypeメソッドとgetTypeメソッドは両方ともjava.lang.Stringを返していることが分かります。

一方、List<String>クラスのコンポーネントだと、getGenericSignatureメソッドがLjava/util/List<Ljava/lang/String;>;になっています。シグネチャの読み方ですが、Lは参照型を表していて、そのクラスがLと;に挟まれた部分になっています。なので、これはList<String>を表しています。

これはgetGenericTypeメソッドで返る値と同じです。

一方でgetTypeメソッドは型パラメータがないListになっていることが分かります。

 

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

なんとパッケージが1つ追加されていました。今のところ定義されているクラスは1つです。

ObjectMethodsクラス

ObjectMethodsクラスが定義しているメソッドは1つ。このメソッドを見ると、このクラスが何のためのクラスか分かりますw

  • static Object bootstrap(MethodHandles.Lookup lookup, String methodName, TypeDescriptor type, Class<?> recordClass, String names, MethodHandle... getters)

bootstrapメソッドといえばINDYです。

Javadocを見ると、Object.equals/hashCode/toStringの3メソッドを生成するとあります。この3つのメソッドというと、先ほどのRecords型で生成されたメソッドがINDYを使っていましたね。どうやら、Records型のクラスを定義すると、このbootstrapメソッドで実行時にこれらのメソッドを生成するようです。ちゃんと追っていないので、もしかしたら違うかもしれませんが 😅

 

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

CompactNumberFormatクラス

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

  • CompactNumberFormat(String decimalPattern, DecimalFormatSymbols symbols, String[] compactPatterns, String pluralRules)

これまでのコンストラクタと比べると、最後のpluralRulesが追加されています。Plural RuleはUnicodeで決められているLanguage Plural Rulesのことです。記述のしかたもUnicodeのドキュメントを見てください。

とはいうものの、CompactNumberFormatオブジェクトを自分でコンストラクタを使って生成することは、ほぼないと思うんですよね。

 

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

LockSupportクラス

ロックのサポートのためのLockSupportクラスですが、スレッドのスケジューリングを行うためカレントスレッドを無効にするparkメソッドがあります。

parkメソッドには引数でブロッカーを指定することもできるのですが、引数なしのオーバーロードもあります。

引数なしのparkメソッドをコールする前に、ブロッカーを設定するためのメソッドが追加されました。

  • statci void setCurrentBlocker(Object blocker)

 

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

SourceVersion列挙型

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

  • RELEASE_14

 

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

ElementKind列挙型

パターンマッチングとRecords型に対応するため定数が追加になっています。

  • BINDING_VARIABLE
  • RECORD
  • RECORD_COMPONENT

BINDING_VARIABLEがinstanceofを使ったパターンマッチングに対応しています。

 

RecordComponentElementインタフェース

こちらもRecords型に対応するために追加されたインタフェースです。定義されているメソッドは3つ。

  • ExecutableElement getAccessor()
  • Element getEnclosingElement()
  • Name getSimpleName()

getAccessorメソッドがコンポーネントのアクセッサ―メソッドに対する要素を返します。getEnclosingElementメソッドはコンポーネントを定義しているRecord型の要素を返します。

 

TypeElementインタフェース

こちらもRecords型に対応するためにメソッドが追加されました。

  • List<? extends RecordComponentElement> getRecordComponents()

 

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

javax.lang.model.utilパッケージではコンパイル時に使用するプログラム要素の扱うためのビジターなどをいろいろと定義しているパッケージです。こちらもRecords型導入にともなってビジターなどが追加されています。

追加されたクラスだけを列挙しておきます。

  • AbstractElementVisitor14
  • AbstractTypeVisitor14
  • ElementKindVisitor14
  • ElementScanner14
  • SimpleAnnotationValueVisitor14
  • SimpleElementVisitor14
  • SimpleTypeVisitor14
  • TypeKindVisitor14

 

java.xml/org.xml.saxパッケージ

ContentHandlerインタフェース

XMLのSAXパーサーで使用するContentHandlerインタフェースにメソッドが追加されました。。

  • void declaration(String version, String encoding, String standalone) throws SAXException)

逆に、今までなぜdeclarationを受け取るメソッドがなかったのかの方が不思議ですね。

ちなみに、このメソッドはdefaultメソッドで定義されていますが、defaultメソッドでは何も行っていません。

 

ということで、Java 14のAPIの変更をまとめてみました。Records型導入による追加が大きいですね。逆にいうと、それ以外のAPI追加はほとんどないのがちょっと寂しい...

 

2019/10/04

Java SE 13: Unmentioned in JEPs

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

Java SE 13 was released on 17th Sep.

In that day, Oracle Code One was held in San Francisco, and Brian Goetz talked about new features of Java SE 13 in Code One keynote.

However, new features of Java 13 are a little. Only 5 JEPs are introduced in Java SE. There are no JEP about library.

2 JEPs are about language specification: one is switch expression (JEP 354) and other is text block (JEP 355).

switch expression was introduced as preview feature in Java 12. It was discussed again, and was re-proposed in Java 13.

Text block is originally JEP 326 Raw String Literal. Text block is also preview feature.

 

In this entry, I'll explain new features, mainly API updates. These are unmentioned in JEPs.

But, I have enough knowledge about security APIs, so I'll skip these updates.

 

Removed APIs

2 methods were removed in Java SE 13.

These are the methods that forRemoval of @Deprecated set true from Java SE 9.

  • java.lang.Runtime.traceInstructions(boolean)
  • java.lang.Runtime.traceMethodCalls(boolean)

As you can see in these methods Javadoc, descriptions of the methods are "Not implemented, does nothing." I don't know why the methods didn't removed until Java 13.

 

APIs Proposed for Removal

Many APIs are poposed for removal in Java 13. I listed APIs that forRemoval of @Deprecated were true.

Package

  • javax.security.cert

Classes

  • javax.security.cert.Certificate
  • javax.security.cert.X509Certificate

Exceptions

  • javax.security.cert.CertificateEncodingException
  • javax.security.cert.CertificateException
  • javax.security.cert.CertificateExpiredException
  • javax.security.cert.CertificateNotYetValidException
  • javax.security.cert.CertificateParsingException

Methods

  • java.lang.String.formatted
  • java.lang.String.stripIndent
  • java.lang.String.translateEscapes
  • javax.net.ssl.HandshakeCompletedEvent.getPeerCertificateChain
  • javax.net.ssl.SSLSession.getPeerCertificateChain

With removal of javax.security.cert package, we should use java.security.cert package instead of javax.security.cert package.

3 methods of String class are new methods in Java 13. However, forRemoval variables are true. Why?

The answer is that these methods are associated with text bloch feature. Because text bloch is preview feature, these methods may be changed when text bloch becames official feature.

3 methods of String class are explained after.

2 methods of javax.net.ssl package are also associated with javax.security.cert package. Return type of HandshakeCompletedEvent.getPeerCertificateChain method is array of X509Certificate class. Insted of getPeerCertificateChain method, we can use getPeerCertificates method that return type is array of Certificate class.

In the same way, we can use SSLSession.getPeerCertificates method instead of getPeerCertificateChain method.

 

New APIs

java.lang package

Java SE 13 supports Unicode 12.1, but the feature is not defined by JEP. In relation to Unicode 12.1 support, some constants are added to 2 classes.

Unicode version supported in Java SE is described in javadoc of java.lang.Character class.

Here is the Character class javadoc of Java SE 13: "Character information is based on the Unicode Standard, version 12.1"

 

Character.UnicodeBlock class

As the name suggests, UnicodeBlock class defines Unicode blocks. UnicodeBlock class adds 9 constants introduced in Unicode 12.0.

  • EGYPTIAN_HIEROGLYPH_FORMAT_CONTROLS
  • ELYMAIC
  • NANDINAGARI
  • NYIAKENG_PUACHUE_HMONG
  • OTTOMAN_SIYAQ_NUMBERS
  • SMALL_KANA_EXTENSION
  • SYMBOLS_AND_PICTOGRAPHS_EXTENDED_A
  • TAMIL_SUPPLEMENT
  • WANCHO

Character.UnicodeScript enum

UnicodeScript enum also defines 4 scripts introduced in Unicode 12.0.

  • ELYMAIC
  • NANDINAGARI
  • NYIAKENG_PUACHUE_HMONG
  • WANCHO

 

String class

As mentioned before, String class defines 3 new methods associated with text block. We can use the methods, but javac compiler alerts for using them.

  • String formatted(java.lang.Object... args)
  • String stripIndent()
  • String translateEscapes()

format method of String class is a static method introduced in J2SE 5, while on the other hand formatted method is a instance method.

jshell>  import java.time.*

jshell> "%s%n".formatted(LocalDate.now())
|  Warning:
|  formatted(java.lang.Object...) in java.lang.String has been deprecated and marked for removal
|  "%s%n".formatted(LocalDate.now())
|  ^--------------^
$1 ==> "2019-09-17\r\n"

jshell>

Both format method and formatted method call format method of java.util.Formatter class internally.

stripIndent method removes line head white-spaces, when text is multi line and meaningless line head white-space.

jshell> var text = "  abc\n" +
   ...> "   def\n" +
   ...> "    ghi"
text ==> "  abc\n   def\n    ghi"

jshell> System.out.println(text)
  abc
   def
    ghi

jshell> System.out.println(text.stripIndent())
abc
 def
  ghi
|  Warning:
|  stripIndent() in java.lang.String has been deprecated and marked for removal
|  System.out.println(text.stripIndent())
|                     ^--------------^

jshell>

When you use text bloch, line head white-spaces are removed automatically.

translateEscapes translate escape sequence into Unicode. For example, "\n" consisting of 2 character is translated into U+000A.

We don't use this method usually, and text block feature uses stripIndent and translateEscape methods.

 

java.nio package

Classes associated with Buffer class are added some methods.

 

Buffer class

Buffer class defined overloaded slice method.

  • Buffer slice(int index, int length)

Existing slice method has no argument, and cuts buffer from current position to limit.

New slice method cut by index and length argument explicitly.

I used both methods with JShell as below:

jshell> var buffer = ByteBuffer.allocate(5)
buffer ==> java.nio.HeapByteBuffer[pos=0 lim=5 cap=5]

jshell> buffer.position(2)
$3 ==> java.nio.HeapByteBuffer[pos=2 lim=5 cap=5]

jshell> var buffer2 = buffer.slice()
buffer2 ==> java.nio.HeapByteBuffer[pos=0 lim=3 cap=3]

jshell> var buffer3 = buffer.slice(2, 3)
buffer3 ==> java.nio.HeapByteBuffer[pos=0 lim=3 cap=3]

jshell>

I used ByteBuffer class because Buffer class is abstract class. We can also use new slice method of other concrete classes such as CharBuffer class.

 

ByteBuffer/CharBuffer/DoubleBuffer/FloatBuffer/IntBuffer/LongBuffer/ShortBuffer class

Each class is added new overloaded get methods put methods.

I explaine the case of ByteBuffer class, but usage is all same.

  • ByteBuffer get(int index, byte[] dst)
  • ByteBuffer get(int index, byte[] dst, int offset, int length)
  • ByteBuffer put(int index, byte[] src)
  • ByteBuffer put(int index, byte[] src, int offset, int length)

Existing get methods reads bytes from current position, or reads 1 byte from specified index. New overloaded get methods read bytres into byte array from specified index.

jshell> var b = new byte[]{0, 1, 2, 3, 4, 5}
b ==> byte[6] { 0, 1, 2, 3, 4, 5 }

jshell> var buffer = ByteBuffer.wrap(b)
buffer ==> java.nio.HeapByteBuffer[pos=0 lim=6 cap=6]

jshell> var bytes = new byte[2]
bytes ==> byte[2] { 0, 0 }

jshell> buffer.get(2, bytes)
$13 ==> java.nio.HeapByteBuffer[pos=0 lim=6 cap=6]

jshell> bytes
bytes ==> byte[2] { 2, 3 }

jshell>

Above code reads two bytes from ByteBuffer object.

In the same way, put methods write bytes from index.

MappedByteBuffer class

Overloaded force methods was added to MappedByteBuffer class.

  • MappedByteBuffer force(int index, int length)

Existing force method has no argument, and write memory-mapped file contents to the file forcibly. New overloaded force method also write memory-mapped file contents by index argument and length argument.

 

java.nio.file package

FileSystems class

FileSystem class defines 3 overloaded newFileSystem methods.

  • FileSystem newFileSystem(Path path)
  • FileSystem newFileSystem(Path path, Map<String, ?> env)
  • FileSystem newFileSystem(Path path, Map<String, ?> env, ClassLoader loader)

newFileSystem method is a factory method of FileSystem object, and uses URI for specifying the file system. newFileSystem also uses Path interface, but Classloader together.

New overloaded newFileSystem method specify the file system by Path ingterface, and use System class loader.

It is a little bit easier to deal with ZIP file or JAR file.as a file system.

jshell> var path = Paths.get("C:\\Program Files\\Java\\jdk-13\\lib\\src.zip")
path ==> C:\Program Files\Java\jdk-13\lib\src.zip

jshell> var fileSystem = FileSystems.newFileSystem(path)
fileSystem ==> C:\Program Files\Java\jdk-13\lib\src.zip

jshell> Files.list(fileSystem.getPath(".")).forEach(System.out::println)
./jdk.zipfs
./jdk.xml.dom
./jdk.unsupported.desktop
./jdk.unsupported
./jdk.security.jgss
./jdk.security.auth
./jdk.sctp
./jdk.scripting.nashorn.shell
./jdk.scripting.nashorn
./jdk.rmic
./jdk.pack
./jdk.net
    <<snip, snip, snip>>

jshell>

 

java.time.chrono package

JapaneseEra class

JapaneseEra indicates Japanese traditional era, and was added new era "REIWA" as constant. As you may know, the costant was backported to Java 8/11/12.

  • REIWA

 

javax.annotation package

ProcessingEnvironment interface

ProcessingEnvironment interface is used for annotation processing.

  • boolean isPreviewEnabled()

isPreviewEnabled method checked to use preview feature. If --enabled-preview option is set, isPreviewEnabled method returns true. If no, it returns false.

 

javax.lang.model package

SourceVersion enum

In every release, a constant is added in SourceVersion enum.

  • RELEASE_13

 

javax.lang.model.element package

ExecutableElement interface

ExecutableElement interface indicateds an element that enables to execute such as method of class/interface and constructor.

  • TypeMirror asType()

asType method returns TypeMirror object that indicates a type.

 

javax.tools package

StandardJavaFileManager interface

javax.tools package includes classes asociated with javac compiler, and StandardJavaFileManager interface is a file manager for the compiler.

  • Iterable<? extends JavaFileObject> getJavaFileObjectsFromPaths​(Collection<? extends Path> paths)

Existing getJavaFileObjectsFromPaths method has an argument as Iterable interface, but new overloaded getJavaFileObjectsFromPaths method uses Collection interface.

On the other hand, old getJavaFileObjectsFromPaths method was deprecated.

 

javax.xml.parsers package

DocumentBuilderFactory class

I didn't imagine that DOM parser was added new APIs!

  • DocumentBuilderFactory newDefaultNSInstance()
  • DocumentBuilderFactory newNSInstance()
  • DocumentBuilderFactory newNSInstance​(String factoryClassName, ClassLoader classLoader)

These 3 methods are factory methods to create DocumentBuilderFactory object using name space.

 

There are some APIs about security, but I don't have enough knowledge about these APIs. So, I skiped them.

2019/09/18

JEPでは語れないJava SE 13

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

アメリカ西海岸時間の9月17日にJava SE 13がリリースされました。

ちょうどサンフランシスコではOracle Code Oneが開催されていて、櫻庭も参加しています。OC1に参加しているのでなかなか書く時間がとれないのですが、恒例となっているのでJEPで提案されている以外のJava SEの変更をまとめておきます。

Java SE 13も変更はかなり少ないです。提案されたJEPは5個。ライブラリに関するJEPはなく、言語使用に関するJEPが2つです。

1つがswitchが文から式になるということ(JEP 354)。Java SE 12でもPreview機能で入っていましたが、再検討されて新たにPreview機能として入っています。値を返す時のyieldが一番の違いです。

もう1つが、テキストブロック(JEP 355) こちらはJava SE 12で入らなかったJEP 326 Raw String Literalの再検討版です。もちろん、テキストブロックもpreview機能なので、今後変更される可能性があります。

 

さて、本題のAPIの方です。こちらもそれほど変更は大きくありません。

今回もABC順にならんでいます。同じように、セキュリティ系のAPIはちゃんと理解していないので、省略してます。

 

廃止になったAPI

Java SE 13では2つのメソッドが廃止になっています。

いずれも、Java SE 9で@DeprecatedのforRemovalがtrueになったメソッドです。

  • java.lang.Runtime.traceInstructions(boolean)
  • java.lang.Runtime.traceMethodCalls(boolean)

Java SE 12以前のJavadocを見ていただければ分かりますけど、この2つのメソッドの説明は"Not implemented, does nothing." なぜ、今まで残していたのか不思議なくらいですねw

 

廃止予定のAPI

廃止予定APIの追加は多いです。

パッケージ

  • javax.security.cert

クラス

  • javax.security.cert.Certificate
  • javax.security.cert.X509Certificate

例外

  • javax.security.cert.CertificateEncodingException
  • javax.security.cert.CertificateException
  • javax.security.cert.CertificateExpiredException
  • javax.security.cert.CertificateNotYetValidException
  • javax.security.cert.CertificateParsingException

メソッド

  • java.lang.String.formatted
  • java.lang.String.stripIndent
  • java.lang.String.translateEscapes
  • javax.net.ssl.HandshakeCompletedEvent.getPeerCertificateChain
  • javax.net.ssl.SSLSession.getPeerCertificateChain

javax.security.certパッケージは、java.security.certパッケージを使うようにということです。javax.security.certパッケージはJava SE 9で@Deprecatedになったのですが、Java SE 13でforRemoval=trueになったということで、廃止することが決まったということです。

Stringクラスの3つのメソッドはJava SE 13で追加されたメソッドです。Java SE 13で追加されたのに、forRemoval=trueというのは、どういうこと?という感じですね。

これらのメソッドの機能については後述しますが、いずれもテキストブロックに関連しているメソッドなのです。つまり、テキストブロックがpreviewから正式な仕様に変更される時に、これらのメソッドも変更されるかもということを示しているわけです。

javax.net.sslパッケージの2つのメソッドもjavax.security.certパッケージに関連したメソッドです。HandshakeCompletedEvent.getPeerCertificateChainメソッドは返り値がX509Certificateクラスの配列なのです。今後はgetPeerCertificateChainメソッドではなく、同じクラスのgetPeerCertificatesメソッドを使うようにします。

SSLSession.getPeerCertificateChainメソッドも同じで、代わりにgetPeerCertificatesメソッドを使うようにします。

 

追加されたAPI

java.langパッケージ

JEPにはなっていないのですが、Java SE 13ではUnicode 12.1をサポートしています。その関連で2つのクラスに定数が増えました。

Character.UnicodeBlockクラス

UnicodeBlockクラスはその名の通り、Unicodeのブロックを表す定数を定義しているクラスです。Unicode 12.0で導入されたブロックが追加になりました。

  • EGYPTIAN_HIEROGLYPH_FORMAT_CONTROLS
  • ELYMAIC
  • NANDINAGARI
  • NYIAKENG_PUACHUE_HMONG
  • OTTOMAN_SIYAQ_NUMBERS
  • SMALL_KANA_EXTENSION
  • SYMBOLS_AND_PICTOGRAPHS_EXTENDED_A
  • TAMIL_SUPPLEMENT
  • WANCHO

Character.UnicodeScript列挙型

enumのUnicodeScriptにもUnicode 12.0で導入されたスクリプトが追加になりました。

  • ELYMAIC
  • NANDINAGARI
  • NYIAKENG_PUACHUE_HMONG
  • WANCHO

 

追記

Oracleの佐藤さんに、Java 13が対応しているUnicodeのバージョンが12.0ではなく、12.1と教えていただきました。本文は修正してあります。

なお、対応しているUnicodeのバージョンはjava.lang.CharaccterクラスのJavadocに記述してあるそうです。Java 13の場合は以下のように表記されています。

Character information is based on the Unicode Standard, version 12.1.

(Java 13 CharacterクラスのJavadocより引用)

 

Stringクラス

前述したように、Stringクラスにはテキストブロック関連のメソッドが3つ追加されました。しかも@DeprecatedのforRemoval=trueなので、コンパイル時に警告が出ます。

  • String formatted(java.lang.Object... args)
  • String stripIndent()
  • String translateEscapes()

J2SE 5でstaticメソッドのformatメソッドが導入されましたが、formattedメソッドはそれのインスタンスメソッド版です。

jshell>  import java.time.*

jshell> "%s%n".formatted(LocalDate.now())
|  警告:
|  java.lang.Stringのformatted(java.lang.Object...)は推奨されておらず、削除用にマークされ ています
|  "%s%n".formatted(LocalDate.now())
|  ^--------------^
$1 ==> "2019-09-17\r\n"

jshell>

formatメソッドもformattedメソッドも内部ではjava.util.Formatterクラスのformatメソッドをコールしています。

stripIndentメソッドは複数行からなる文字列の先頭文字がホワイトスペースだった場合、前詰めした文字列に変換するためのメソッドです。

jshell> var text = "  abc\n" +
   ...> "   def\n" +
   ...> "    ghi"
text ==> "  abc\n   def\n    ghi"

jshell> System.out.println(text)
  abc
   def
    ghi

jshell> System.out.println(text.stripIndent())
abc
 def
  ghi
|  警告:
|  java.lang.StringのstripIndent()は推奨されておらず、削除用にマークされています
|  System.out.println(text.stripIndent())
|                     ^--------------^

jshell>

なお、テキストブロックを使用すると、勝手に前詰めされます。コンパイル時にこのメソッドを使用しているかどうかまでは調べていないので、ぜひ調べてみてください。

translateEscapesメソッドはエスケープシーケンスを変換するメソッドです。

たとえば、"\n"という2文字で表されているエスケープシーケンスをU+000Aに置き換えます。

とはいうものの、このメソッドの使い道がイマイチよく分からないんですよね。文字列を出力する時には、置き換えられていたので、意識することはなかったのですが... どういう時に使うんだろう?

なお、このメソッドではUnicodeのエスケープは置き換えを行わないそうです。

 

java.nioパッケージ

Buffer系のクラスにメソッドがいろいろと追加されました。

Bufferクラス

Bufferクラスにはsliceメソッドがオーバーロードされました。

  • Buffer slice(int index, int length)

今まで使われていたsliceメソッドは引数なしでcurrent positionとlimitの間を切り出すメソッドでした。

オーバーロードされたのは明示的にインデックスと長さを指定して切り出すために使用されます。

実際には内部で、引数なしのsliceと同じような処理行っているので、使い勝手をよくするためのメソッドということですね。

jshell> var buffer = ByteBuffer.allocate(5)
buffer ==> java.nio.HeapByteBuffer[pos=0 lim=5 cap=5]

jshell> buffer.position(2)
$3 ==> java.nio.HeapByteBuffer[pos=2 lim=5 cap=5]

jshell> var buffer2 = buffer.slice()
buffer2 ==> java.nio.HeapByteBuffer[pos=0 lim=3 cap=3]

jshell> var buffer3 = buffer.slice(2, 3)
buffer3 ==> java.nio.HeapByteBuffer[pos=0 lim=3 cap=3]

jshell>

同じ切り出しを引数なしのsliceメソッドと、引数ありのsliceメソッドでやってみました。

なお、Bufferクラスはアブストラクトクラスなので、ByteBufferクラスを使用しています。

もちろん、他のCharBufferクラスなどでも引数ありsliceメソッドがオーバーロードされています。

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

それぞれのクラスにgetメソッドが2つ、putメソッドが2つオーバーロードされています。

Bufferクラスに追加されたsliceメソッドは返り値の方がBufferクラスなのですが、ByteBufferクラスなどでオーバーロードされたメソッドは返り値の型がそれぞれのクラスなので、Bufferクラスでは定義できないのでした。

ただ、使い道はどのクラスでも同じなので、ここではByteBufferクラスで説明します。

  • ByteBuffer get(int index, byte[] dst)
  • ByteBuffer get(int index, byte[] dst, int offset, int length)
  • ByteBuffer put(int index, byte[] src)
  • ByteBuffer put(int index, byte[] src, int offset, int length)

今までのgetメソッドはpositionの位置から読み込みを行うか、インデックスを指定して1バイト読み込むかのどちらかしかありませんでした。新しくオーバーロードされたgetメソッドはインデックスを使用して第2引数のバイト配列に読み込みを行います。

jshell> var b = new byte[]{0, 1, 2, 3, 4, 5}
b ==> byte[6] { 0, 1, 2, 3, 4, 5 }

jshell> var buffer = ByteBuffer.wrap(b)
buffer ==> java.nio.HeapByteBuffer[pos=0 lim=6 cap=6]

jshell> var bytes = new byte[2]
bytes ==> byte[2] { 0, 0 }

jshell> buffer.get(2, bytes)
$13 ==> java.nio.HeapByteBuffer[pos=0 lim=6 cap=6]

jshell> bytes
bytes ==> byte[2] { 2, 3 }

jshell>

ここでは、インデックス2から2バイト読み込んでます。

putメソッドも同じで、今まではpositionから書き込みを行うか、インデックスを使用して1文字の書き込みを行うだけだったのが、インデックスを使用してバイト配列で書き込みが行えるようになりました。

MappedByteBufferクラス

MappedByteBufferクラスにはforceメソッドがオーバーロードされました。

  • MappedByteBuffer force(int index, int length)

今までのforceメソッドは引数なしで、メモリにマップされた内容を強制的にファイルに書き出すというメソッドでした。オーバーロードされたforceメソッドはインデックスと長さを指定して強制的にファイルに書き出しを行います。

 

java.nio.fileパッケージ

FileSystemsクラス

FileSytemクラスでは、3種類のnewFileSystemメソッドがオーバーロードされています。

  • FileSystem newFileSystem(Path path)
  • FileSystem newFileSystem(Path path, Map<String, ?> env)
  • FileSystem newFileSystem(Path path, Map<String, ?> env, ClassLoader loader)

newFileSystemメソッドはFileSystemクラスのファクトリメソッドなのですが、今までは場所を指定するのにURIを使用していました。Pathインタフェースも使えたのですが、クラスローダ―を指定する必要がありました。

Java 13で追加された3種類はどれもPathインタフェースで場所を指定します。

ZIPファイルやJARファイルをファイルシステムとみなして扱うような時に使用するメソッドなのですが、パスで指定できるようになったので、少しだけ扱いやすくなりました。

jshell> var path = Paths.get("C:\\Program Files\\Java\\jdk-13\\lib\\src.zip")
path ==> C:\Program Files\Java\jdk-13\lib\src.zip

jshell> var fileSystem = FileSystems.newFileSystem(path)
fileSystem ==> C:\Program Files\Java\jdk-13\lib\src.zip

jshell> Files.list(fileSystem.getPath(".")).forEach(System.out::println)
./jdk.zipfs
./jdk.xml.dom
./jdk.unsupported.desktop
./jdk.unsupported
./jdk.security.jgss
./jdk.security.auth
./jdk.sctp
./jdk.scripting.nashorn.shell
./jdk.scripting.nashorn
./jdk.rmic
./jdk.pack
./jdk.net
    <<以下、省略>>

jshell>

 

java.time.chronoパッケージ

JapaneseEraクラス

日本の元号を表すクラスですが、令和が定数として追加されました。メジャーバージョンとしてはJava 13からということです。

  • REIWA

javax.annotationパッケージ

ProcessingEnvironmentインタフェース

アノテーションの処理を行う時に使用するインタフェースがProcessingEnvironmentインタフェースです。

  • boolean isPreviewEnabled()

プレビュー機能を使用する時にはコンパイル/実行時に--enable-previewオプションを使用します。isPreviewEnabledメソッドは--enable-previewが指定されていればtrue、いなければfalseを返します。

 

javax.lang.modelパッケージ

SourceVersion列挙型

毎度のお約束ですが、定数にJava 13が追加されました。

  • RELEASE_13

 

javax.lang.model.elementパッケージ

ExecutableElementインタフェース

ExecutableElementインタフェースは、クラスやインタフェースのメソッド、コンストラクタなど実行できる要素を表すためのインタフェースです。

  • TypeMirror asType()

ExecutableElementインタフェースで表している実行要素がメソッドなのか、コンストラクタなのか、初期化子なのかを判別するために使用します。

 

javax.toolsパッケージ

StandardJavaFileManagerインタフェース

javax.toolsパッケージはコンパイラ系のクラスが定義されているのですが、そこで使用するファイルマネージャがStandardJavaFileManagerインタフェースです。

  • Iterable<? extends JavaFileObject> getJavaFileObjectsFromPaths​(Collection<? extends Path> paths)

これまで使われていたgetJavaFileObjectsFromPathメソッドは引数の型がIterableインタフェースだったのですが、Collectionインタフェースを引数にとるメソッドがオーバーロードされました。

逆に引数の型がIterableインタフェースの方は@Deprecatedになっています。

 

javax.xml.parsersパッケージ

DocumentBuilderFactoryクラス

まさか、この期におよんでDOMパーサーに機能が追加されるとは思いもよりませんでしたw

  • DocumentBuilderFactory newDefaultNSInstance()
  • DocumentBuilderFactory newNSInstance()
  • DocumentBuilderFactory newNSInstance​(String factoryClassName, ClassLoader classLoader)

いずれも名前空間を認識したDocumentBuilderFactoryオブジェクトを生成するファクトリメソッドです。

 

他にもセキュリティ系のAPIに追加がありますが、よく分からないので省略します。

ということで、Java 13のAPIの変更をまとめてみましたが、やっぱり変更は少ないですね。

 

2019/06/15

JJUG CCC 2019 Spring

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

もう1月も前ですが、5月18日にJJUG CCC 2019 Springが開催されました。Oracle Codeの次の日ですね。

今回はProject LoomのFiberについてプレゼンしました。資料はこちら。

Fiberはいわゆる軽量スレッドです。

プレゼンではなぜ軽量スレッドが必要なのかという部分について、かなり時間をかけて説明しました。

OSに強く結びついたThreadを使った場合、コンテキストスイッチにコストがかかります。時間もかかるし、メモリも多く使用します。

これは、スレッドごとに用意されるJVM Stackをすべて退避させたり、もどしたりする必要があるからです。JVM Stackは、そのスレッドでコールされているメソッドコールのスタックで、メソッドで使われるローカル変数やオペランドスタックも含みます。

また、OSがスレッドのスケジューリングを管理しているため、コンテキストスイッチがいつ行われているかもJava側からは管理することができません。

そのために求められているのが、JVMで管理する軽量スレッド、つまりFiberというわけです。

これに対し、Fiberを使えば、ワーカースレッド上で動作するため、スケジューリングはJVMで管理でき、状態の退避用のメモリも少なくてすみます。

Fiberでは、スケジューリングには既存のFork/Join Frameworkが使用されます。コンテキストスイッチには処理の中断、再開のための仕組みが必要ですが、これはContinuationで行います。Continuationはいわゆる継続を実現させるための仕組みですが、JavaのContinuationは限定継続になります。

プレゼンの中ではContinuationをかなり強引にwait-notifyAllと同じようなものと説明してしまったため、誤解させてしまったのではないかと反省しています。もちろん、Continuationもwait-notifyAllも処理の中断・再開をするという機能はありますが、一般的なContinuationでできることは処理の中断・再開だけではありません。ちょっと説明不足でした。

しかし、今のところProject LoomではContinationを積極的に活用するようなシナリオはないように見えます。現状は、I/O待ちのためにパフォーマンスが落ちていることに対して、Fiberを使ってI/O待ちをなるべく解消することがメインの目的のようです。

これは、処理の中断・再開を行うFiberのparkメソッド、unparkメソッドがデフォルトのアクセス制御であることからも分かります。park/unparkを使用しているのは、ReentrantLockクラスなどのロック系のクラスや、java.nio.channelパッケージのソケット通信などのクラスなどです。

プレゼンの中では前日に行ったOracle Codeのデモについても説明しました。JDKに含まれているHTTPサーバーであるcom.sun.net.httpserver.HttpSErverクラスを使用して、Fiberを指定しているだけです。これだけで、既存のExecutorServiceインタフェースを使用した場合よりもスレッド数やスループットが向上しました。

とはいうものの、現状Fiberはそこまで速くありません。

コンテキストスイッチが起きないようにうまくタスク分けして、I/O待ちも多重化するなどして待たなければいけないスレッドを最小化するなどのチューニングを行えば、Fiberより高いパフォーマンスを得ることができます。

ただ、それをするには設計やチューニングなどの高度な知識や経験が求められます。Fiberを使うことで誰でも簡単にパフォーマンスを向上させられるということが、Fiberの意義の1つなのではないかと感じています。

また、Fiberのさらなるパフォーマンス向上のためにJVM Stackを操作することも考えられているようなので、今後に期待したいですね。

 

さて、以下は会場やsli.doでの質問とその回答です。

Q Fiberの中断前と再開後でワークスレッドが変わることがあるのか?

A. あります。

キターーーーーーー 「この分野は素人なのですが」な質問!!!

なんとか答えられてよかったですw

さて、現状はFiberはシリアライザブルではないのですが、シリアライザブルにする計画があります。このため、ワークスレッドが変化することもありますし、処理途中のFiberを他のCPUに移動させてそこで再開というシナリオも考えられます。

質問した伊藤さんも心配されていますが、ロガーのようにスタックトレースを保持させるようなものは、Fiberにするとやばいかもしれません。

Q. ある時点の処理で中断していたものを複数回再実行することはできるか?

A. 一般的な継続だとこれができるのですが、今のところJavaのFiberでは計画されていないようです。

Fiberは継続を行うための状態管理に既存のJVM Stackをそのまま使っています。そのため、再実行を行うには、JVM Stackを操作するバイトコードが必要になるはずですが、そこまでやるとかなり大がかりになってしまうためかもしれません。

Q. ワークスレッドは自動的に用意されるのか?

A. されます。

デフォルトではForkJoinPoolを利用してワークスレッドの割り当てを行っています。もちろん、他のスレッドプールに置き換えることもできます。

Q. OSによらないThreadがFiberだとしたら、Green Threadのようなもの?

A. まさにGreen Threadです。

Javaの初期のころ、Solaris向けのJavaはネイティブのスレッドではなく、JVMが管理するスレッドを使用していたのですが、それがGreen Threadです。

Q. Kotlinのcoroutineと何が違うのか?

A. Kotlinのことをよく知らないのですが、同じような機能のようですね。

Q. Continuationのscopeがよく分からない。コンテキストスイッチを行いたい複数のFiberからなるグループのようなもの?

A. 継続処理を行いたい範囲をしめすものです。

どこからでも自由に中断・再開を行うのは難しいので、継続ができる範囲を決めているという感じです。Fiberも内部でスコープを持っていて、Fiberのタスク処理の中だけで継続を行っています。

Q. ThreadとFiberの使い分け指針が知りたい

A. I/Oの待機やロックの取得を含む非同期処理であればFiberを使うのがよいと思います。

計算処理だけであるならパラレルストリームやFork/Join Frameworkを使いましょう。それ以外だったらThreadになると思いますが、今でもExecutorServiceにタスクを渡すのが主で、Threadを直接使うことはまずないはずです。

Q. 既存のThreadをFiberに置き換えるイメージがつかめません。ExecutorServiceでThreadを使うようなことはFiberでもできるのでしょうか。

A. Fiber.scheduleメソッドがExecutorService.submitメソッドのような感じです。

Fiber.scheduleメソッドはstaticメソッドなので、ExecutorServiceのようにExecutorsクラスでExecutorServiceインスタンスを生成して、それからsubmitするまでを行っているような感じです。

Q. Loom入りのJDKを含んだDocker Imageは公開されているか?

A. 私が調べたときにはなかったです。

Docker上でLoomのJDKをビルドしようとするとなぜか落ちてしまうので、私はローカルな環境でビルドしてからそれをコピーするDockerfileを作ってDockerのイメージを作ってました。

Q. Loomのリリース予定は未定だとしても、現時点での目標とかはあるのでしょうか?

A. 明確なリリース予定時期がFiberを作っている人たちの間ではあるのかもしれませんが、私には分からないです。

少なくとも、Windowsで動作できるようにならないとリリースはできないので、そこが最低限の目標となるのではないでしょうか。