Puzzle 31: Ghost of Looper

复合赋值运算的返回类型

复合赋值符可能导致narrowing primitive conversions,因为按照JLS 15.26.2的定义:

  • A compound assignment expression of the form E1 op= E2 is equivalent to E1 = (T)((E1) op (E2)), where T is the type of E1, except that E1 is evaluated only once.

所以对于下面这段代码:

i = -1

while ( i != 0) {

i >>>= 1;

}

当i为short,int型的情况是不同的。一个一个分析:

1. i为short型

代码片段可以依据复合运算的定义还原为

short i = -1;

while (i != 0) {

i = (short) (i >>> 1);

}

执行的时候,由于位移操作要求对不是int/long型的整数转型成为int/long型再进行运算,所以i首先宽转型为int,从0xffff变成0xffffffff,然后作无符号右移,成为0x7fffffff,再窄转型为short的0xffff,它不等于0,所以循环继续,以下过程和第一次循环一样。这个循环不会终止。

除了short型,i为byte型也会出现这样的情况。

char无法声明 char i = -1;只能写成 char i = (char) -1;输出结果类似于i为int型时。

2. i为int型

代码片段可以依据复合运算的定义还原为

int i = -1;

while (i != 0) {

i = i >>> 1;

}

执行的时候,没有强制转型了,都是int范围内,i从0xffffffff变成0x7fffffff,不等于0,继续,变成0x3fffffff,如此,最后变成0,终止循环。

除了int型,long型也会出现这样的情况。

书中观点:

  1. 对于Java使用者是:不要将byte/char/short用于复合运算符出现的场景。
  2. 对于Java语言设计者:窄化转型不能silently地进行,更进一步,是否允许byte/char/short参与复合运算符出现的场景也值得讨论。

我赞同第一条,对第二条持保留意见。

  1. JLS 5.1.3 Narrowing Primitive Conversions
  2. JLS 15.26.2 Compound Assignment Operators