次のコードは、"Hello World!"という出力を生成します(いや、本当に試してみてください)。
public static void main(String... args) {
// The comment below is not a typo.
// \u000d System.out.println("Hello World!");
}
これは、JavaコンパイラがUnicode文字`u000d
を改行として解析し、変換されてしまうためです。
public static void main(String... args) {
// The comment below is not a typo.
//
System.out.println("Hello World!");
}
その結果、コメントが実行されてしまうのです。
これは、悪意のあるコードや邪悪なプログラマーが考え出すあらゆるものを"hide"するために使用することができるので、なぜコメントで許可されているのでしょうか?
なぜJavaの仕様でこのようなことが許されているのでしょうか?
Unicodeのデコードは、他のいかなる語彙の翻訳よりも先に行われます。このことの主な利点は、ASCIIと他のエンコーディングの間を行き来することが簡単になることです。コメントがどこで始まってどこで終わるのかを理解する必要もありません。
JLS セクション 3.3](https://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#jls-3.3)で述べられているように、これにより、ASCII ベースのツールであれば、ソースファイルを処理することができます。
[...] Javaプログラミング言語では、Unicodeで書かれたプログラムをASCIIに変換する標準的な方法が規定されており、プログラムをASCIIベースのツールで処理できる形式に変更することができます。[...]
これにより、Javaプラットフォームの重要な目標であるプラットフォームの独立性(サポートする文字セットの独立性)が基本的に保証されます。
ファイル内の任意の場所にUnicode文字を書けるというのは素晴らしい機能であり、非ラテン言語のコードを文書化する際のコメントでは特に重要です。セマンティクスを微妙に阻害するという事実は、(不幸な)副次的な効果に過ぎません。
このテーマでは多くのゲッチュがあり、Joshua BlochとNeal GafterによるJava Puzzlersには次のようなバリエーションがありました。
これは合法的なJavaプログラムですか?合法なJavaプログラムですか? となります。 \u0070\u0075\u0062\u006c\u0069\u0063\u0020\u0020\u0020\u0020 \u0063\u006c\u0061\u0073\u0073\u0020\u0055\u0067\u006c\u0079 \u007b\u0070\u0075\u0062\u006c\u0069\u0063\u0020\u0020\u0020 \u0020\u0020\u0020\u0020\u0073\u0074\u0061\u0074\u0069\u0063 \u0076\u006f\u0069\u0064\u0020\u006d\u0061\u0069\u006e\u0028 \u0053\u0074\u0072\u0069\u006e\u0067\u005b\u005d\u0020\u0020 \u0020\u0020\u0020\u0020\u0061\u0072\u0067\u0073\u0029\u007b \u0053\u0079\u0073\u0074\u0065\u006d\u002e\u006f\u0075\u0074 \u002e\u0070\u0072\u0069\u006e\u0074\u006c\u006e\u0028\u0020 \u0022\u0048\u0065\u006c\u006c\u006f\u0020\u0077\u0022\u002b \u0022\u006f\u0072\u006c\u0064\u0022\u0029\u003b\u007d\u007d
(このプログラムは、何の変哲もない "Hello World" プログラムであることがわかります)。)
この問題の解答では、次のように指摘されています。
さらに言えば、このパズルは前の3つのパズルの教訓を補強するものでもあります。**ユニコードエスケープは、他の方法では表現できない文字をプログラムに挿入する必要がある場合には不可欠です。それ以外の場合は避けてください。
ソースはこちら。Java: コメントでコードを実行する?!
なぜなら、`u
のエスケープは、プログラムがトークン化される前に、対応するUnicode文字に一律に変換されるからです。 同じように、//
の代わりに˶ˆ꒳ˆ˵
を使って、コメントを開始することもできます。
これは IDE のバグで、この行をシンタックスハイライトして、コメントの終了を明確にするべきです。
また、これは言語の設計上のミスでもあります。 これに依存しているプログラムが壊れてしまうので、今は修正できません。 U+0000-007Fの範囲内の文字を生成することを禁止するか、その両方を行うべきでした。 これらのセマンティクスのいずれかがあれば、コメントが \u
エスケープによって終了するのを防ぐことができ、 \u
エスケープが有用なケースを妨げることはありません。これには、非ラテン文字のコメントをエンコードする方法として、コメント内で sexual
エスケープを使用することも含まれます。 (ただし、どのような文脈でも \u
エスケープを対応する文字として表示するエディタや IDE を私は知りません)。
Cファミリにも同様の設計ミスがあり、コメント境界が決定される前にバックスラッシュ改行が処理されるため、例えば
// this is a comment \
this is still in the comment!
コンパイラのプログラマがトークン化とパーシングについて考えるように、トークン化とパーシングについて考えることに慣れていると、このような特定の設計上の誤りを犯しやすく、修正するのが遅すぎるまで誤りであることに気づかないことがあるということを説明するために、この話をします。 基本的には、形式的な文法をすでに定義していて、誰かが構文上の特殊なケースを思いついたとします。例えば、三角記号、バックスラッシュ・改行、ASCIIに限定されたソースファイルに任意のUnicode文字をエンコードする、などです。このような特殊なケースを使用することに意味があるようにトークナイザーを再定義するよりも、トークナイザーの前に変換パスを追加する方が簡単です。
1 衒学者のために。これは、任意の長さの行を持つコードをパンチカードに機械的に強制的にフィットさせることができるという根拠に基づいています。 しかし、これは間違った設計上の決定でした。
これがデザイン上のミスであることは、@zwolさんと同じですが、私はそれ以上に批判的です。
文字列や char のリテラルでは便利なものですが、それ以外には存在してはいけないものです。他のエスケープと同じように扱われるべきで、"˶‾᷅᷅˵
は正確に"˶‾᷄˵
を意味するものでなければなりません。
誰もそれを読むことはできません。
同じように、プログラムの他の部分でも \uxxxx
を使う意味はありません。唯一の例外は、おそらく、非アスキー文字を含むように強制されているパブリック API でしょう。
1995年当時、設計者には理由がありましたが、20年後の今、この選択は間違っていたようです。
(読者への質問 - なぜこの質問に新しい票が集まり続けるのでしょうか? この質問はどこか人気のあるところからリンクされているのでしょうか?)。