半斤八两

1.半斤

许多程序员都会认为该迷题中的第一个表达式(x += i)只是第二个表达式(x = x + i)的简写方式。但是这并不十分准确。这两个表达式都被称为赋值表达式[JLS 15.26]。第二条语句使用的是简单赋值操作符(=),而第一条语句使用的是复合赋值操作符。(复合赋值操作符包括 +=、-=、*=、/=、%=、<<=、>>=、>>>=、&=、^=和|=)Java语言规范中讲到,复合赋值 E1 op= E2[JLS 15.26.2 ]。等价于简单赋值E1 = (T)((E1)op(E2)),其中T是E1的类型,除非E1只被计算一次
换句话说,复合赋值表达式自动地将它们所执行的计算的结果转型为其左侧变量的类型。如果结果的类型与该变量的类型相同,那么这个转型不会造成任何影响。然而,如果结果的类型比该变量的类型要宽,那么复合赋值操作符将悄悄地执行一个窄化原始类型转换[JLS 5.1.3 ]。因此,我们有很好的理由去解释为什么在尝试着执行等价的简单赋值可能会产生一个编译错误。
为了说得具体一些,并提供一个解决方案给这个谜题,假设我们在该谜题的两个赋值表达式之前有下面这些声明:
short x = 0;
int i = 123456;
复合赋值编译将不会产生任何错误:
x += i; // 包含了一个隐藏的转型!
你可能期望x的值在这条语句执行之后是123,456,但是并非如此l,它的值是-7,616。int类型的数值123456对于short来说太大了。自动产生的转型悄悄地把int数值的高两位给截掉了。这也许就不是你想要的了。
相对应的简单赋值是非法的,因为它试图将int数值赋值给short变量,它需要一个显式的转型:
x = x + i; // 不要编译——“可能会丢掉精度”
这应该是明显的,复合赋值表达式可能是很危险的。为了避免这种令人不快的突袭,请不要将复合赋值操作符作用于byte、short或char类型的变量上int类型的变量上时,要确保表达式右侧不是long、float或double类型。在将复合赋值操作符作用于float类型的变量上时,要确保表达式右侧不是double类型。这些规则足以防止编译器产生危险的窄化转型。。在将复合赋值操作符作用于
总之,复合赋值操作符会悄悄地产生一个转型。如果计算结果的类型宽于变量的类型,那么所产生的转型就是一个危险的窄化转型。这样的转型可能会悄悄地丢弃掉精度或数量值。对语言设计者来说,也许让复合赋值操作符产生一个不可见的转型本身就是一个错误;对于在复合赋值中的变量类型比计算结果窄的情况,也许应该让其非法才对。
 


2.八两

就像前面的谜题一样,这个谜题也依赖于有关复合赋值操作符的规范中的细节。二者的相似之处就此打住。基于前面的谜题,你可能会想:符合赋值操作符比简单赋值操作符的限制要少一些。在一般情况下,这是对的,但是有这么一个领域,在其中简单赋值操作符会显得更宽松一些。
 
 复合赋值操作符要求两个操作数都是原始类型的,例如int,或包装了的原始类型,例如Integer,但是有一个例外:如果在+=操作符左侧的操作数是String类型的,那么它允许右侧的操作数是任意类型,在这种情况下,该操作符执行的是字符串连接操作[JLS 15.26.2]。简单赋值操作符(=)允许其左侧的是对象引用类型,这就显得要宽松许多了:你可以使用它们来表示任何你想要表示的内容,只要表达式的右侧与左侧的变量是赋值兼容的即可[JLS 5.2]。
 
 你可以利用这一差异来解决该谜题。要想用 += 操作符来执行字符串连接操作,你就必须将左侧的变量声明为String类型。通过使用直接赋值操作符,字符串连接的结果可以存放到一个Object类型的变量中。
 
 为了说得具体一些,并提供一个解决方案给这个谜题,假设我们在该谜题的两个赋值表达式之前有下面这些声明:
 
 Object x = “Buy ”;
 
 String i = "Effective Java!";
 
 简单赋值是合法的,因为 x + i 是String类型的,而String类型又是与Object赋值兼容的:
 
 x = x + i;
 
 复合赋值是非法的,因为左侧是一个Object引用类型,而右侧是一个String类型:
 
 x += i;
 
 这个谜题对程序员来说几乎算不上什么教训。对语言设计者来说,加法的复合赋值操作符应该在右侧是String类型的情况下,允许左侧是Object类型。这项修改将根除这个谜题所展示的违背直觉的行为。

 

你可能感兴趣的:(java,String,object,Integer,语言,float)