JavaSE 7でメソッド名に使えなくなった文字

パッケージJava製品開発担当の大です。こんにちは。
朝晩はだいぶ肌寒くなって、秋らしい空気になってきましたね。

前回も書いたとおり、現在HOSでは製品のJavaSE 7での動作検証を進めています。JavaSE 7で早く使いたい方、申し訳ありませんがいましばらくお待ちください。

さて、検証している上でちょっと困ったことが出てきました。従来動いていたテストコードの一部が、JavaSE 7ではコンパイルもできなくなってしまったのです。これはJavaSE 7の不具合か?と思い、調査してみました。

日本語のテストメソッド名

製品のクラス名やメソッド名では使いませんが、開発時のユニットテストのメソッド名やクラス名は、最近は、基本的に日本語で書くようになりました。これまで日本語メソッド名で特に問題が起こっていなかったことと、テストのレポートの出力がアルファベットのキャメルケースに比べ圧倒的に見やすいからです。

今回のコンパイルエラーの原因は、これらの日本語のテストメソッド名のいくつかで、

@Test
public void ほげ・ふがのテスト() {
    ....
}

のように、「・」(ナカグロ: U+30FB)を使っていたことでした。

Character.isJavaIdentifierPart

CharacterクラスのisJavaIdentifierPartメソッドを使用して、ナカグロがJavaの識別子(の最初の文字以外)に使用可能かどうか確かめてみます。

System.out.println(Character.isJavaIdentifierPart('・'));

このコードをJavaSE 6で実行するとtrueに、JavaSE 7で実行するとfalseになりました。
Javadocによると、次のどれかがtrueであればこのメソッドはtrueを返すようです。

  • 汎用文字である
  • 通貨記号である ('$' など)
  • 連結句読点文字である ('_' など)
  • 数字である
  • 数値汎用文字である (ローマ数字文字など)
  • 連結マークである
  • 非スペーシングマークである
  • isIdentifierIgnorable(codePoint) がその文字について true を返す

しかし、この仕様自体はJavaSE 6でも7でも違いはないようです。

ソースを覗いてみると、どうやらここから内部的に呼ばれているjava.lang.CharacterData00というクラスの実装が、6と7でだいぶ変わっているらしいことがわかりました。

UnicodeData

どのようにして、この修正が行われたのかを調べてみます。OpenJDKのプロジェクトからJava SE 7のソースを取得して見てみると、このjava.lang.CharacterData00というクラスはビルド時に自動生成されていることがわかりました。

  • 元となるテンプレート: jdk/make/tools/GenerateCharacter/CharacterData00.java.template
  • データ: jdk/make/tools/UnicodeData/以下
  • ジェネレータ: jdk/make/tools/src/build/tools/generatecharacter/GenerateCharacter.java

こんな感じみたいです。テンプレートやジェネレータにはナカグロ(U+30FB)がハードコードされているようには見えなかったので、ナカグロを持つデータ(UnicodeData.txt)のログを見てみます。

$ hg log UnicodeData.txt
チェンジセット:   3157:13bbabfee6d4
ユーザ:           peytoia
日付:             Wed Nov 24 14:13:37 2010 +0900
要約:             7002398: Apply Corrigendum #8 for Unicode 6.0.0

チェンジセット:   3150:3207aa4438fc
ユーザ:           peytoia
日付:             Wed Nov 17 01:02:20 2010 +0900
要約:             6959267: Support Unicode 6.0.0

チェンジセット:   1134:1729e34a0287
ユーザ:           peytoia
日付:             Fri Apr 10 11:51:36 2009 +0900
要約:             6404304: RFE: Unicode 5.1 support

チェンジセット:   0:37a05a11f281
タグ:             jdk7-b24
ユーザ:           duke
日付:             Sat Dec 01 00:00:00 2007 +0000
要約:             Initial load

JavaSE 7の開発中、Unicode5.1、Unicode 6.0と段階的にサポートされてきたようです。5.1サポート時とその前の差分を見てみると。。。

$ hg diff -r0 -r1134 UnicodeData.txt
(snip)
@@ -8632,7 +10239,7 @@
 30F8;KATAKANA LETTER VI;Lo;0;L;30F0 3099;;;;N;;;;;
 30F9;KATAKANA LETTER VE;Lo;0;L;30F1 3099;;;;N;;;;;
 30FA;KATAKANA LETTER VO;Lo;0;L;30F2 3099;;;;N;;;;;
-30FB;KATAKANA MIDDLE DOT;Pc;0;ON;;;;;N;;;;;
+30FB;KATAKANA MIDDLE DOT;Po;0;ON;;;;;N;;;;;
 30FC;KATAKANA-HIRAGANA PROLONGED SOUND MARK;Lm;0;L;;;;;N;;;;;
 30FD;KATAKANA ITERATION MARK;Lm;0;L;;;;;N;;;;;
 30FE;KATAKANA VOICED ITERATION MARK;Lm;0;L;30FD 3099;;;;N;;;;;
(snip)

ありました!30FBの「Pc」が「Po」に変更されています。

このUnicodeData.txtは、見覚えがありました。unicode.orgで配布しているやつですね。
unicode.orgのサイトで、このPcとかPoの意味を調べてみると、

Pc Connector_Punctuation a connecting punctuation mark, like a tie
Po Other_Punctuation a punctuation mark of other type

と、ありました。つまり、変更前は「連結句読点文字(Pc)」だったのが、「その他の句読点文字(Po)」に変更されたため、Javaの識別子として使用できる文字ではなくなったということですね。

Unicodeの変更履歴を読んでみると、2005年にリリースされた4.1の変更で以下のような記述を見つけました:

U+30FB KATAKANA MIDDLE DOT and U+FF65 HALFWIDTH KATAKANA MIDDLE DOT were changed from gc=Pc to gc=Po. See PRI #55

結構前に変更されていたんですね。JavaSE 6のリリースより前みたいですが、JavaSE 6はこの変更前のUnicodeをベースにしてたんでしょうか。

結論

つまり、JavaSE 7でメソッド名にナカグロ(U+30FB)が使えなくなったのは、Unicodeの仕様変更によるものであり、不具合ではないということです。疑ってすみませんでした。

おまけ

原因を調べてたときに、「もしかしてナカグロ以外の文字でも使えなくなっているものがあるんでは?」と思い、とりあえずJIS X 0213:2004の文字を全部チェックしてみました。

(表示にはシーオーリポーツ ビュアーが必要です。)

識別子に使用可能な文字の比較

識別子に使用可能な文字の比較(クリックで拡大)

背景が青いのが識別子に使える文字、白いのが使えない文字です。JavaSE 6では1面1区6点のナカグロが使えることになっていますが、JavaSE 7では使えないことがわかります。(ちなみに、上記のUnicodeの変更点でU+30FBのほかにU+FF65というのも出てきますが、JIS X 0213:2004上はどちらも同じ文字です)。

このファイルはXMLテキストですので、手軽にdiffツールで差分を見ることができます。結果、ナカグロ以外に使えなくなっている文字はありませんでした。