2013/05/07

Android:toLowerCase/toUpperCaseに注意

最近のLintは賢くなって、String.toLowerCaseやtoUpperCaseをLocale引数無しで使用すると警告を出すようになりました。
Locale指定がないと、予期せぬ事態を招く可能性があるからです。
本稿はLocaleについて少し触れてみたいと思います。

突然ですが、下記の評価式はtrue or falseどちらを返すでしょうか。
"YUKI".toLowerCase().equals("yuki")
答えは true 、といきたいところですがfalseにもなり得るのです。

評価結果がfalseとなるのは、たとえばデバイスの言語設定がトルコ語であった場合です。


トルコ語では"I"の小文字は"i"ではありません。
そのため、"YUKI"を小文字化しても"yuki"にはならないためfalseと評価されます。
つまり、Localeを指定しないString型の文字列比較では、言語設定(Locale)によって評価結果が変化する可能性があるということです。
これを回避するには、ロケールに依存しないようにtoLowerCase(Locale.ENGLISH)を使用します。

String.toLowerCase(Locale)リファレンス
http://docs.oracle.com/javase/6/docs/api/java/lang/String.html#toLowerCase(java.util.Locale)


それでは、下記の評価式はtrue or falseどちらを返すでしょうか。
"YUKI".equalsIgnoreCase("yuki")
答えは、ロケールに関係なく true が返されます。

先述のtoLowerCaseの件を考えると、equalsIgnoreCase(String)もロケールに依存するように思えます。
しかし、equalsIgnoreCaseの評価はロケールに依存しないのです。

これは、equalsIgnoreCaseで用いられるのは、String.toLowerCase(), String.toUpperCase()ではなく、
Character.toLowerCase(char), Character.toUpperCase(char)であるためです。

Characterクラスが持つそれぞれのメソッドはロケールに依存したケースマッピングをサポートしていません。
そのため、言語設定がトルコ語になっても評価に影響することがないのです。

String.equalsIgnoreCase(String)リファレンス
http://docs.oracle.com/javase/6/docs/api/java/lang/String.html#equalsIgnoreCase(java.lang.String)


i18nの敷居の高さが伺える事例でした。
ただ、下記のような状況があり得るというのには多少違和感を感じますね。
 "YUKI".toLowerCase().equals("yuki")=false
 "YUKI".equalsIgnoreCase("yuki")=true

その他参考:
Local:
http://developer.android.com/reference/java/util/Locale.html#default_locale

以上です。