63257


[String型を==で比較したときの挙動が予想と違う](https://ja.stackoverflow.com/a/63099/2808)
の回答では(3)の形が登場しています。

普段、私はIDEの自動補完で変数に `final`
を付与しているので、たとえ(3)の形で記述しても最終的に上記(2)の形になります(正確には変数`final_ne_and_final_ko`も`final`にもなりますが)。

`final` の有無で結果が変わる、というのが意外だったのですが、これはどの仕様に拠るものでしょうか。  
(IDEの `final` 自動補完によって挙動が変わるようなことが他に何かあるだろうか、というのが気になっています。 `String`
以外でも考慮すべき事項があるならやっかいだな、と…)

(1)のリテラル連結については [3.10.5. String Literals - Example 3.10.5-1. String
Literals](https://docs.oracle.com/javase/specs/jls/se13/html/jls-3.html#jls-3.10.5)
にそのものの例がありましたが、これと(2)が同じになり(3)とは異なるのがなぜか、という点について探し当てることができませんでした。

**ソースコード**

[code]

    // (1) 文字列リテラルの連結: true
    String literal_neko = "ネコ";
    String literal_ne_ko = "ネ" + "コ";
    
    System.out.println("literal_neko == literal_ne_and_ko: " + (literal_neko == literal_ne_ko)); // true
    
    // (2) finalなStringの連結: true
    final String final_ne = "ネ";
    final String final_ko = "コ";
    String final_ne_and_final_ko = final_ne + final_ko;
    
    System.out.println("literal_neko == final_ne_final_ko: " + (literal_neko == final_ne_and_final_ko)); // true
    
    // (3) 非finalなStringの連結: false
    String ne = "ネ";
    String ko = "コ";
    String ne_and_ko = ne + ko;
    
    System.out.println("literal_neko == ne_and_ko: " + (literal_neko == ne_and_ko)); // false
    
    
    Runnable r = () -> {
        // 蛇足になりますが: ne, ko, neko いずれもfinalではないが実質finalなので参照可能
        String neko_runnable = ne + ko;
        System.out.println("literal_neko == neko_runnable: " + (literal_neko == neko_runnable)); // false
        System.out.println("ne_and_ko == neko_runnable: " + (ne_and_ko == neko_runnable)); // false
    };
    r.run();
    
[/code]


CC BY-SA 4.0
2020-02-22T04:51:13.393
63250
2020-02-22T07:14:35.993
2020-02-22T07:14:35.993
3060
2808
question
java
String型を==で比較したときの挙動がfinal/非finalで違う


(1)についてはお書きの通りです。

JavaではすべてのStringリテラルは同じConstant
Poolを参照して実質的に`intern()`済みであるということが言語仕様で決まっているので、リテラル同士やリテラルを代入した変数の`==`による比較は`true`になります。

その前提に加えて(1)の`String literal_ne_ko = "ネ" + "コ"`はStringリテラルの`+`演算子による連結なので

> Strings computed by constant expressions (§15.28) are computed at compile
> time and then treated as if they were literals.

<https://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#jls-3.10.5>

^ が適用されて、コンパイル時に`String literal_ne_ko =
"ネコ"`として解釈されるので、リテラルは`intern()`済みと合わせて`==`の比較が`true`になります。

  

(2)と(3)についてですが、本来は`String ne_and_ko = ne +
ko;`は(1)の場合と異なり、実行時に評価されて新しいインスタンスを生成するので、`literal_neko ==
ne_and_ko`は`false`になります（(3)の結果）。

ではなぜ(2)では`true`になるかというとお察しの通り`final`の有無によります。

> A variable of primitive type or type String, that is final and initialized
> with a compile-time constant expression (§15.28), is called a constant
> variable.

<https://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.4-300>

^ とあるように、プリミティブ型およびString型については`final`かつConstant
Expressions（Stringリテラルが該当します）で初期化される変数は`constant variable`になります。

なので`final`の`final_ne`と`final_ko`は結局Constant Expressions（Stringリテラルと同等 =>
<https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.28>
より）になるので

[code]

    String final_ne_and_final_ko = final_ne + final_ko;
    
[/code]

^ これはコンパイル時に

[code]

    String final_ne_and_final_ko = "ネ" + "コ";
    
[/code]

と解釈され、さらにこれは(1)のケースと同様に

[code]

    String final_ne_and_final_ko = "ネコ";
    
[/code]

とコンパイルされるので、`literal_neko == final_ne_and_final_ko`の評価は`true`となります。


CC BY-SA 4.0
2020-02-22T06:08:22.333
63257
2020-02-22T06:08:22.333
5519
63250
answer