きしだのはてな このページをアンテナに追加 RSSフィード

2018-06-11(月) Java9、10でStringの+=に副作用があるバグ

[] Java9、10でStringの+=に副作用があるバグ 00:23  Java9、10でStringの+=に副作用があるバグを含むブックマーク

Java 9、10でStringの+=にバグがあるということがStack OverFlowで報告されていました。

Why does array[idx++]+=”a” increase idx once in Java 8 but twice in Java 9 and 10? - Stack Overflow


どういうバグかというと「s[i++] += i + ""」のようなコードが正しく動かないというものです。

次のコードを実行してみます。

public class PlusEqual {
    public static void main(String[] args) {
        System.out.print(System.getProperty("java.version"));

        String[] s = {"aa", "bb"};
        int i = 0;
        s[i++] += i + "";
        System.out.printf(" %s %d%n", Arrays.toString(s), i);
    }
}

Java 8では期待通りに動きます。

1.8.0_151 [aa1, bb] 1

ところが、JDK10.0.1では次のようになります。

10.0.1 [bb2, bb] 2

おもしろいのは、target=8とするとJDK10でもJava8と同じように動くところです。

10.0.1 [aa1, bb] 1

これは、「s[i++] += i + ""」が「s[i++] = s[i++] + i + ""」のように扱われていると考えることができます。

JDK11ea17で修正されています。JDK10.0.2はどうなるのかな。バックポートはされる予定。Java9はメンテナンスリリースは公開されないので、そのまま。

JDK-8204322 ”+=” applied to String operands can provoke side effects - Java Bug System


で、このバグレポートはJEP280に関連づけられてるので、+演算子をdynamicInvoke使う実装にしたときにエンバグしたっぽいですね。

JEP 280: Indify String Concatenation

Java8までのJVMでは もちろん文字列結合のdynamicInvokeに対応していないので、target=8としたときには問題が発生しない、と。

なので、Java9やJava10.0.1でtarget=8したくない場合は-XDstringConcat=inline をつけてコンパイルすれば問題を回避することができるようです。