如何使 E1 += E2 非法,而 E1 = E1 + E2 是合法的?

我一直在阅读Bloch和Gafter的Java Puzzlers,并进入了拼图10(Tweedledee)。这个谜题的本质是

为变量提供声明,以便这是一个法律声明:xi

x = x + i;

但这不是:

x += i;

根据这本书,解决这个问题的方法看起来像这样:

Object x = "Buy ";
String i = "Effective Java!";

该书声称,在运算符中,只有当左侧表达式具有类型时,右侧表达式才能是任何类型。但是,我尝试运行此代码,并且它编译并运行没有任何问题。+=String

然后我深入研究了Java语言规范。第 15.26.2 节讨论了两种情况:当左侧表达式是数组访问表达式时,当它不是数组访问表达式时。如果左侧操作数表达式不是数组访问表达式,则 JLS 不会说明左侧表达式是字符串。如果是这样,这部分表示:

如果 T 是引用类型,则它必须是 String。因为类 String 是最终类,所以 S 也必须是 String。因此,对于复合赋值运算符,有时简单赋值运算符不需要运行时检查。

❖ 数组组件的保存值和右侧操作数的值用于执行由复合赋值运算符(必然为 +=)指示的二进制运算(字符串串联)。如果此操作突然完成,则赋值表达式会出于同样的原因突然完成,并且不会发生赋值。

此处的 T 是在编译时确定的左侧操作数的类型,S 是选定的数组组件。所以我想我会把我的代码修改成这样:

Object[] x = {new Object()};
String i = "Effective Java!";
x[0] += i;

但是,即使此代码编译和运行也没有任何问题,即使甚至不是远程.new Object()String

为什么会发生这种情况?这是否意味着Java编译器偏离了JLS?还有可能以某种方式解决原始难题吗?


答案 1

尝试使用javac<1.4.2,它也可以在那里工作。

这是不同版本之间的更改。1.4.2 的更改(x += i;之前允许,此后不允许):
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4642850

这是正确的,因为JLS 2。版本定义:

所有复合赋值运算符都要求两个操作数都是基元类型,但 += 除外,如果左操作数是 String 类型,则 += 允许右操作数为任何类型的操作数。

7 的更改(x += i;以前不允许,此后允许):
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4741726

这是正确的,因为JLS 3。版本(请参阅 http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.26 删除了以前的先决条件)


只是一个小小的编辑:我没有看到任何修复/解决Java 7.0_10难题的方法


答案 2

我有以下内容,它将给定的解决方案显示为正确答案:

public class testIt
{
  public static void main(String args[])
  {
    new testIt();
  }

  public testIt()
  {
    Object x = "Buy";
    String i = "Effective Java!"

    x += i;

    x = x + i;
  }
}

当我编译这个我得到

testIt.java:  incompatible types
found:     java.lang.Object
required:  java.lang.String;

  x += i;
  ^
1 error