Java Puzzlers 小噺 No.11

2024/12/03に公開

概要

Java Puzzlers[1][2] という本で面白い問題を見つけて、Java の仕様を改めて調べてみたという感じの記事です。

この記事では、「Puzzle 11: The Last Laugh」を取り上げます。

きっかけ

たまたま、図書館で見かけて、読んだら面白く、社内の LT 会でこの問題とその仕様についての話をしてみたら、ウケが良かったので、記事にしました。

本題

「Puzzle 11 : The Last Laugh」は以下のソースコードです。[4]

LastLaugh.java
public class LastLaugh {
    public static void main(String args[]) {
        System.out.print("H" + "a");
        System.out.print('H' + 'a');
    }
}

とくに考えなければ、

Output...?
Ha
Ha

と出そうですが、実際は、

Output!!
Ha
169

と出ます。

(☉ε ⊙ノ)ノ「え、嘘!」
( ゚Д゚)「なんで!」

と思った方は、ぜひそのまま読み進めてください。

ε- (´ー`*)「フッ、そんなの当たり前じゃん」
(´-_ゝ-`)「なぜわからないのかわからない」

などと思った方は、リンク元にお戻りいただくか、このタブをそっ閉じしてください。

解説

結論を先に述べると以下の2点です。

  • プリミティブ型 char の値の実体は整数値
  • Stringchar における + の扱い方の違い

それぞれ、説明していきます。

プリミティブ型 char の値の実体は整数値

Java において、char の実体は 16 ビット符号なし整数です。[5]
なので、整数値でいうと、0 から 65535 までの値を保持できます。

参考:各プリミティブ型の取りうる値の範囲

ここで言いたいこととしては、横柄な言い方をすると、
char はシングルクォーテーションで囲って定義するので文字に見えるけど、実質は整数値だということです。

今回のコード上の Ha はそれぞれ ASCII コードで整数に表現すると 7297 となります。

Stringchar における + の扱い方の違い

String 型においての + は文字列連結として働きますが、
char 型における + は文字連結ではなく、足し算として働きます。

参考:+ に関する扱いについて

なので、冒頭の結果の通り、'H' + 'a'72 + 97 となり、169 と表示されることになります。

チョットだけ発展

ここで、更に考えると Stringchar とを + で結ぶとどうなるかということです。

結論は、先の参考リンク先に書かれている通りです。

If only one operand expression is of type String, then string conversion (§5.1.11) is performed on the other operand to produce a string at run time.

(和訳)
もし1つのオペランド式が String 型の場合、もう一方のオペランドに対して実行時に文字列変換(§5.1.11)が行われ、文字列が生成されます。

要は、+ を挟むどちらかが、String 型であれば、もう片方は対応する文字列変換[6]がされて、その 2 つの文字列連結になるということです。

具体的には、String + charchar + String の場合は、char は単純に文字列に変換されたのちに、文字連結されます。

System.out.print("H" + 'a'); // Ha
System.out.print('H' + "a"); // Ha

また、評価順を考えると、String + char + charchar + char + String では結果が変わります。

System.out.print("Ha" + 'H' + 'a'); // HaHa
System.out.print('H' + 'a' + "Ha"); // 169Ha

これも、ここまで読まれていれば理由はわかると思いますが、最初の 2 項の評価結果がそれぞれ違うからですね。
要は、最初の 2 項を評価しますので、それぞれ "Ha" + 'H' は文字列 "HaH" となり、'H' + 'a' は数値 169 となる。
その後、3 項目の評価になるので、それぞれ "HaH" + 'a'169 + "Ha" となる。そして、上記のコメントのところの文字列が表示される流れです。

さいごに

今回の問題を考えることで、Java におけるプリミティブ型の仕様と演算子 + の仕様について再認識できました。

とはいえ、実務で char を使う機会は少ないと思いますので、この記事自体がすぐに役に立つことは難しいかもしれませんが、 Java 言語仕様に関して興味を持ってもらえれば嬉しいなと思っています。

参考リンク

参考文献

  • Joshua Bloch & Neal Gafter (2005). Java puzzlers traps, pitfalls, and corner cases Addison-Wesley Professional. (ジョシュア・ブロック ニール・ガフター 柴田 芳樹 (2005). Java Puzzlers 罠、落とし穴、コーナーケース 桐原書店)
脚注
  1. 参考文献にも書いていますが、原著は 2005 年に Joshua Bloch 氏と Neal Gafter 氏によって書かれ、同年に柴田 芳樹氏による翻訳も桐原書店より出版されました。 ↩︎

  2. もっというと、翻訳本はもともとピアソン・エデュケーション時代に出版されていました。その後、ピアソン桐原時代があったりして、現在は絶版になっています。出版年からでもお察しいただける通り、Java は J2SE 5.0 時代なので、そのあたりを考えるとなんか感慨深いです。 ↩︎

  3. 時間があれば程度ですね。 ↩︎

  4. ソースコードは公式で公開されています。詳細は参考リンクの書籍公式ページ内のリンクをご確認ください。 ↩︎

  5. そもそもでいうと、char は Unicode(UTF-16) を表すからですね。 ↩︎

  6. 詳細は文字列変換についてを参考してください。簡単に言うと、プリミティブ型はそれに相当するラッパークラスにオートボックスしてから、toString() で文字列に変換されます。 ↩︎

BABY JOB  テックブログ

Discussion