Why does array[idx++]+=“a” increase idx once in Java 8 but twice in Java 9 and 10?
对于一个挑战,一个代码高尔夫同伴写了以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
当在Java 8中运行此代码时,我们得到以下结果:
1 2 3 | 1 4 7 10 13 16 19 22 25 28 31 34 37 40 43 46 49 52 55 58 61 64 67 70 73 76 79 82 85 88 91 94 97 100 2 5 8 11 14 17 20 23 26 29 32 35 38 41 44 47 50 53 56 59 62 65 68 71 74 77 80 83 86 89 92 95 98 101 3 6 9 12 15 18 21 24 27 30 33 36 39 42 45 48 51 54 57 60 63 66 69 72 75 78 81 84 87 90 93 96 99 |
号
当在Java 10中运行此代码时,我们得到以下结果:
1 2 3 | 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 52 54 56 58 60 62 64 66 68 70 72 74 76 78 80 82 84 86 88 90 92 94 96 98 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 52 54 56 58 60 62 64 66 68 70 72 74 76 78 80 82 84 86 88 90 92 94 96 98 100 102 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 52 54 56 58 60 62 64 66 68 70 72 74 76 78 80 82 84 86 88 90 92 94 96 98 100 |
使用Java 10的编号完全关闭。那么这里发生了什么?它是Java 10中的一个bug吗?
评论的后续行动:- 这个问题是在用Java 9或更高版本编译时出现的(我们在Java 10中找到它)。在Java 8上编译该代码,然后运行在Java 9或任何稍后版本中,包括Java 11的早期访问,给出预期的结果。
- 这种代码是非标准的,但根据规范是有效的。它是由KevinCruijssen在高尔夫挑战中的讨论中发现的,因此遇到了奇怪的用例。
Didier我发现这个问题可以用更小更容易理解的代码来重现:
1
2
3
4
5
6
7
8
9
10。
在Java 8中编译时的结果:
1evaluated当在Java 9和10中编译时:
1
2evaluated
evaluated。
这个问题似乎仅限于字符串连接和赋值运算符(
+= ,其中一个表达式的副作用是左操作数,如array[test()]+="a" 、array[ix++]+="a" 、test()[index]+="a" 或test().field+="a" 中的操作数。要启用字符串连接,至少一个边的类型必须为String 。尝试在其他类型或构造上复制此内容失败。
这是
1 | array[i++%size] += i +""; |
它是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 21: aload_2 22: iload_3 23: iinc 3, 1 26: iload_1 27: irem 28: aload_2 29: iload_3 30: iinc 3, 1 33: iload_1 34: irem 35: aaload 36: iload_3 37: invokedynamic #5, 0 // makeConcatWithConstants:(Ljava/lang/String;I)Ljava/lang/String; 42: aastore |
号
其中,最后一个
1 2 3 4 5 | 21: aload_2 // load the array reference 22: iload_3 // load 'i' 23: iinc 3, 1 // increment 'i' (doesn't affect the loaded value) 26: iload_1 // load 'size' 27: irem // compute the remainder |
它大致对应于表达式
A compound assignment expression of the form
E1 op= E2 is equivalent toE1 = (T) ((E1) op (E2)) , whereT is the type ofE1 , except thatE1 is evaluated only once.
号
因此,对于表达式
所以是的,这是一个错误。
一些更新:这个bug在JDK11中被修复,并且有一个到JDK10的后端端口(但不是JDK9,因为它不再接收公共更新)。
Aleksey Shipilev在JBS页面上提到(在这里的评论中还有@didierl):
Workaround: compile with
-XDstringConcat=inline
号
这将恢复到使用