メンチカツには醤油でしょ!!

AWS/Java/Node.js/Spreadsheets/Docker/Jenkins/コミュニティ・勉強会レポを主とした技術系ブログ

java.util.regex.Patternで取ったmatcherのgroupが取得できない

マッチしてるはずなのにIllegalStateException thrown: No match found

たとえば、ある文字列が「画面ID + 定型文字列」であるとして、ここから画面IDを抜き取るという実装を正規表現で行う場合…

jshellで実行してみると

まずは文字列の定義をします。この場合は"Bean"が定型文字列です。

jshell> String actionBeanName = "ScreenIdBean";
actionBeanName ==> "ScreenIdBean"

次に正規表現を定義します。任意の文字列で始まり1文字以上、そして後ろにBeanというパターンです。

jshell> Pattern p = Pattern.compile("^(.+)(Bean)$");
p ==> ^(.+)(Bean)$

matcherメソッドでMatchesオブジェクトを取得しgroup(1)を呼ぶと…なんとエラー!

jshell> p.matcher(actionBeanName).group(1)
|  java.lang.IllegalStateException thrown: No match found
|        at Matcher.group (Matcher.java:645)
|        at Matcher.group (Matcher.java:604)
|        at (#4:1)

確認のためにmatches()を呼ぶもtrue…

jshell> p.matcher(actionBeanName).matches()
$5 ==> true

再度group()を取っても同じくNo match foundエラーです。

一致しているはずなのに取れない理由は…

Matcherのmatches()を呼んだあと、そのオブジェクトでgroupを呼ぶ必要があります。
つまり下記のように、まずmを変数に代入して

jshell> java.util.regex.Matcher m = p.matcher(actionBeanName);
m ==> java.util.regex.Matcher[pattern=^(.+)(Bean)$ region=0,12 lastmatch=]

そのオブジェクトmのmatches()を呼んだ後に

jshell> m.matches()
$7 ==> true

groupを呼ぶと、目的の文字列が抽出できました。

jshell> m.group(1)
$8 ==> "ScreenId"

groupを呼ぶには、一旦matches()を呼ばないといけない

正規表現が必ず一致すると明確な場合、エラーチェックとなるmatches()を省略しがちですが、ここが落とし穴でした。

本来であれば下記のように安全な(?)コーディングを行うので、起こりえないです。

String actionBeanName = "ScreenIdBean";
Matcher m = PATTERN.matcher(actionBeanName);
if (m.matches()) {
String screenId = m.group(1);
// 何かしらの処理.
} else {
// マッチしなかった場合の処理.
}

と、必ずエラーチェックしてたから今まで知らなかったと言い訳しておきます^^;

参考

stackoverflow.com

「JDK 11リリース記念:今知っておくべきJDK 11の重要ポイント」参加

JJUGに参加しました

最初レポートとしてまとめようと思ったのですが、スライドが秀逸すぎたので自分のコメントはちょい足し程度にしました。

JJUGナイトセミナー「JDK 11リリース記念:今知っておくべきJDK 11の重要ポイント」

Java Is Still Free; Javaは今も無償です

冒頭でこちらの資料の紹介がありました。ちょうどこの日に、日本語訳が出たそうです。なぜか未だにすべてのJavaが有償化されると勘違いしている方々が多く、こういうドキュメントが発行されることとなったようです。要約バージョンの部分はわずか2ページですから、よく読んでおきましょう。

docs.google.com

↓ 原文

Java Is Still Free - Google Document

JDK 11リリースなので改めて「新しいJDKリリースモデル解説 (サマリー&アップデート)」 by 伊藤 敬さん

今までのOracle JDKから、Oracle OpenJDK(無償)とOracle JDK(有償)に分かれた話。
そして、OracleJavaサポートのロードマップの話です。
なお先に引用のとおり、Java全体としては他のJDKも選択肢として存在しています。

www.slideshare.net

Java 11 : サポートとVM機能 編 by 久保田 祐史さん

Javaのサポートについての、本当にFrequentlyなFAQが載っています。
そして後半はJava 11のVM機能についてです。

www.slideshare.net

・いつまでに乗り換えるべきか。次のLTSであるJava 17は2021/09とまだ先なので、Java 8の配信サポートが切れる前が良さそう。

・OpenJDKは商用に耐えうる。ただ、そもそもの8⇒11へのマイグレに痛みを伴う可能性あり。

・無償ならAdoptOpenJDK(4年サポート)が良さそう。0 day attackを勘案する場合は、Oracle OpenJDKを選択肢に入れるとよい。

Java 11:APIの変更点 編 by きしだなおきさん

スライドは後日とのことなので、私のメモを取り急ぎ ↓↓

JEP

* JEP 181: Nest-Based Access Control
  - インナークラスの話。nested classではprivateメンバもアクセス可だった。
  - Collections.emptyList() の戻り値に
    .isNestmateOf()
    .getNestHost()
    .getNestMembers()
    などを試してみるとよいとのこと。
* JEP 309: Dynamic Class-File Constants
  - 一般に想像する(?)static finalフィールドではなく、class-file上の定数のこと。
  - Class-File ConstantsにinvokeDynamicのような機構を。
  - CONSTANT_Dynamics
  - バイトコード上の定数の自由度があがる。
  - ただし現時点でのJavaコンパイラでは利用していない。
* JEP 320: Remove the Java EE and CORBA Modules
  - JavaEEとCORBA関連モジュールの削除。
  - JAX-WS -> jaxws-api
  - JAXB -> jaxb-api
  - JAF -> javax.activation-api
  - Common Annotations -> javax.annotation-api
  - JTA -> javax.transaction-api
  をそれぞれ利用すること。
* JEP 321: HTTP Client (Standard)
  - 概要
    - モダンなAPI
    - HTTP/2
    - Reactive
  - 手順としては
    - 基本的な設定をClientで。
    - Requestでリクエストの設定。
    - async対応も。
* JEP 323: Local-Variable Syntax for Lambda Parameters
  - Lambda内の変数でvarが使えるようになり、アノテーションも付けれる。

JEPに載ってないAPIの追加・削除・挙動変更

* APIの追加
  - 文字列関連
    - String.repeat
      "aa".repeat(3) ⇒ "aaaaaa"
    - String.isBlank
      " ".isBlank ⇒ 全角も加味してtrueが返る
    - String.strip /String.stripLeading /String.stripTrailing
      こちらも全角対応。
    - String.lines
      StringのStreamが返る。"LINE1¥nLINE2".lines().count() ⇒ 2
    - Character.toString(int)
        Character.toString(65) ⇒ "A"
  - I/O
    - Files
      - isSameContent, writeString, readString などの小回りの良い便利メソッド追加。
    - null I/O ⇒ dev/nullのようなもの
      - Reader.nullReader()
      - Writer.nullReader()
      - InputStream.nullReader()
      - OutputStream.nullReader()
  - Path.of
    Paths.of が Path.of にも追加された。
  - Util
    - Collection.toArray(IntFunction)
      List.of("aaa").toArray(new String[1]) が
      List.of("aaa").toArray(String[]::new) と書けるようになった。
    - Predicate.not
      filterの否定に利用
        List.of("aaa", "").stream().filter(String::isEmpty).toArray();
      の逆が
        List.of("aaa", "").stream().filter(Predicate.not(String::isEmpty)).toArray();
      と書けるようになった。
    - Optional, OptionalInt, OpttionalLong, OptionalDouble
       - isEmpty
* API削除
  - Thread.destroy
  - Thread.stop(Throwable)
  - javax.security.auth.Policy
* 挙動変更
  - ArrayIndexOutOfBoundsException
    - エラーメッセージが "Index 1 out of bounds for length 0." のようになる。
  - 新元号対応
    - JapaneseDate.of(JapaneseEra.HEISEI, 31, 5, 1);
      でエラーとなる。(そんな平成は無いというエラー)
      JapaneseDate.from(LocalDate.of(2020, 1, 1));
      ⇒ Japanese NewEra 2-01-01
    - Java 12.0.1 (2019/04/25ぐらい?)で反映。

JDK11で修正されたバグ

* var のバグ
  var attrs = List.of("size", 1);
  のようなコードでdebug情報付きの時にコンパイラが落ちてしまうバグの解消。
  原因はObject, Serializable, Comparableなどの複数のinterfaceの型推論がはたらくため。
* Stringの+= のバグ
  s[i++] += "foo"
  のように配列の要素に++を入れつつ+=を行うと、JDK10では
  s[i++] = s[i++] + "foo"
  と解釈されてしまうバグの解消。
* TimSort のバグ
  - Arrays.sort / List.sort / Collections.sort が対象。
  - 67108864以上の要素がある場合の特定のケース。
  ArrayIndexOutOfBoundsExceptionが発生することがあるバグの解消。

Java12で入りそうな機能の話

* JEP 326: Raw String Literals

`
foo
foo
foo
` ⇒ "¥nfoo¥nfoo¥n"

* JEP 325: Switch Expressions
まだ実装されていないらしい。調べた感じだとこんなの↓

switch (day) {
    case MONDAY                       -> System.out.println("Off");
    case TUESDAY, WEDNESDAY, THURSDAY -> System.out.println("Work");
    case FRIDAY                       -> System.out.println("Drink");
    case SATURDAY, SUNDAY             -> System.out.println("Holiday");
}

* String.indent / align
Raw String LiteralsよるStringの便利メソッドの追加.

`
foo
foo
`.align(0) ⇒ "foo¥nfoo¥n"

さいごに

Javaは今も無償です。

名古屋Javaユーザグループ2018年4月に参加してバイトコードについて考えた #nagoya_jug

地方(?)イベント初参加

よく"出張のついでに地方のイベントに参加"なんて話を聞くことがあり、そんな都合のいい事があるかいな、と思っていましたが、幸運なことにそれが訪れたので初参加です。(勝手がわからないので緊張しますね)

ngo-java.connpass.com

smogamiさん: NJUG紹介・参加者の自己紹介

全員自己紹介するのって初めてだったので緊張。
「飲み歩きが趣味で、バーでたまたま隣に居た人がPHPerでtype safetyでケンカしかけた」など全PHPerを敵に回すようなブッコミをしてしまいスミマセン。事実に変わりないですが、ちょっと面白い事言おうとしただけです。
スクリプト言語好きですよ!)

sh-ogawa さん: テストコードと運用

テストとCI。カバレッジ100%を目指すなとか、エラーを放置するなとか、そんな話。

Sonar Qube使ってみよう。Dockerリポジトリはここ。
https://hub.docker.com/_/sonarqube/

質問でWork In Progress Pull Request開発の話が出ましたが、これってちゃんと用語があってドキュメントに纏まっていたんですね。
https://ben.straub.cc/2015/04/02/wip-pull-request/
PRのタイトルが変わってhookが取れないと質問していましたが、そうなのかな。。

www.slideshare.net

bleit-tift さん: JVM入門

はじめて書いたプログラミング言語コンパイラという猛者の、JVMについてのとてもわかりやすい解説。

CASLとかZ80アセンブリとかをやっていた身としては、バイトコードも結局脳内デバッグとあまり変わらないな、という印象だったのですが、後の発表でさっそく活きる場面が。

skrbさん: Project Amberの話

Project CoinがSmall Lang. ChangesならAmberはSmall to Big Lang. Changesということで、各JEPにより言語仕様がどう変わっていくのか、という話。

Javaは最近では他の言語仕様の影響や、周辺ライブラリの影響を受けて進化している印象がありますが、今回も同じです。

内容については既に公開されているスライドを見て頂くとして、感想としてはJEP 326 Raw String Literalsに変数のバインディングができるともっといいのにと思いました。

var weather = "sunny";
System.out.println(`It's a ${weather} day today.`);

(コンパイルしたらnew StringBuilder().append().append().toString()String.formatと等価になるとか)

www.slideshare.net

yuji38kwmtさん: Java7からJava8に移行した際にClassCastException発生

 現象とかはスライドを見て頂くとして。

qiita.com

P14. Java8ではhogeの型パラメータはchar、とあるが実際はObjectで変わらず。
これはEclipse Compilerの間違い。

バイトコード上でcheckcastが入るJava8だとエラー。
会場でもなぜJava7だと入らないんだろう…もしかしてこっそり修正された?との話も。

下の画像で比較してみました。左側がJava7、右側がJava8です。
確かに右側(Java8)には、59行目にcheckcastが入っているのが確認できます。
ここで、実行時エラーになるわけですね。

f:id:ryoichi0102:20180417123025p:plain

更に見ていくと、ジェネリクスのメソッドhoge()内の定数"hoge"が、Java7ではString、Java8ではcharになっていることが確認できます。

#4 = String #13 // hoge
#13 = Utf8 hoge
#3 = Class #22 // "[C"
#22 = Utf8 [C

コンパイル時のジェネリクスの型消去の際にStringではなくchar[]で生成されるようになっています。こちらも気になります。

f:id:ryoichi0102:20180417123039p:plain

先のセッションでバイトコードの話を聞いていたので、アレルギーなく読めました^^

まとめ

  • javapだいじ
  • javapが少し身近になった

内容の組み合わせや順序がとても良かったように感じました。
懇親会まで行けたら本当は良かったのですが、他にもやることがあったので退散してしまった。会場挙手時点で50%ぐらい挙がってたのが意外(?)でした。