日々常々

ふつうのプログラマがあたりまえにしたいこと。

Java21に上げたらテスト通らないんだが

まだJava17の子がいたので、雑にJava21に上げてみたらテスト落ちたの。 「なんでそんなとこ落ちんの?」って感じだったのでメモ。

何変わったんだろ

正規表現\b の扱いが変わってたっぽいです。

Java17でやるとこうで、

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

jshell> "あ".matches(".\\b")
$1 ==> true

Java21だとこうなります。

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

jshell> "あ".matches(".\\b")
$1 ==> false

わお。

ちなみに "a".matches(".\\b") はどっちも true です。

いつ変わったんだろ

API仕様を見比べるとJava18 までは 単語境界 だったのが、 Java19単語境界 : (?:(?<=\w)(?=\W)|(?<=\W)(?=\w)) (単語以外の文字が単語文字を省略するロケーション) に変わっていました。

多分この辺なんだろうなぁと思いつつ、SDKMAN!にもJava18-20はもうないので、挙動での確認は面倒だなぁと。 別にバージョン特定しなくてもいいかなぁと思いながらコミットログ眺めてたらこれが目に入りました。API仕様と同時の19でよさそう。

とりあえず17から21にしたら変わっちゃいます。正規表現を使っているとこのテストは見直しとくといいかもです。

なおAPI仕様の説明は Java24でまた変わる みたいです。

ちなみに

UNICODE_CHARACTER_CLASS したらJava21でもJava18以前と同じ結果の true にはなります。ここに関しては。他も変わるからまぁあれだ。

// Java17, 21どちらも同じ
jshell> Pattern.compile(".\\b", Pattern.UNICODE_CHARACTER_CLASS).matcher("あ").matches()
$2 ==> true


// java21
jshell> Pattern.compile(".\\b").matcher("あ").matches()
$3 ==> false

// java17
jshell> Pattern.compile(".\\b").matcher("あ").matches()
$3 ==> true

もともとUNICODE_CHARACTER_CLASS つけなきゃ \w"あ" とかに使えなかったわけで。

// Java17, 21どちらも同じ
jshell> Pattern.compile("\\w", Pattern.UNICODE_CHARACTER_CLASS).matcher("あ").matches()
$4 ==> true

// Java17, 21どちらも同じ
jshell> Pattern.compile("\\w").matcher("あ").matches()
$5 ==> false

\w\b の対応が取れていなかったのがちゃんと取れるようになったようです。

まとめ

雑に正規表現使ってると踏むかもしれない挙動変更がJava19にありました。

挙動変更は更新追ってなければテストでしか気づけないから、やっぱテストは大事。

バージョン跨いだテストは早めに回しておきたいところ。できるところはやってるんだけど、Gradleのバージョン上げれてなくてできてなかったんだよね……

宣伝

Java24が2025-03-18にGA予定です。

Java24なので次回関ジャバは3/24に行います。

kanjava.connpass.com

Java24の話をしつつも、17以降で「これは実務に影響あったなぁ」とか22以降の「これは使いそうだなぁ」みたいな話をしようと思っています。