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

Posted

技术标签:

【中文标题】如何使 E1 += E2 非法而 E1 = E1 + E2 是合法的?【英文标题】:How do I make E1 += E2 illegal while E1 = E1 + E2 is legal? 【发布时间】:2012-12-22 12:09:31 【问题描述】:

我一直在阅读 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 是一个 final 类,S 也必须是 String。因此,运行时检查是 有时需要简单的赋值运算符,从不需要 复合赋值运算符。

❖ 数组组件的保存值和右手边的值 操作数用于执行二元运算(字符串连接) 由复合赋值运算符指示(这必然是 +=)。如果这个操作突然完成,那么赋值表达式 出于同样的原因突然完成并且没有分配发生。

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

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

但即使new Object() 甚至不是远程String,也可以毫无问题地编译和运行这段代码。

为什么会这样?这是否意味着 Java 编译器偏离了 JLS?是否还有可能以某种方式解决最初的难题?

【问题讨论】:

您是否尝试过创建一个覆盖toString 的对象以打印到stdout/stderr? 你确定哪个应该编译?或者这本书有错别字。例如,如果 x 和 i 是字节,则 x += i; 有效,但 x = x + i; 无效。 您确定您实际测试的是x += i 而不是i += x?我的意思是 (String) += (Object) 将很容易编译,而 (Object) += (String) 将无法编译。 @Tinctorius 你不必创建一个对象来打印到标准输出,结果是一个看起来像这样的字符串:java.lang.Object@5a4b4b50Effective Java!。所以串联很顺利。 @Malcolm:我只是打算将其用于(惰性)调试,以了解 Java 实际在做什么。 【参考方案1】:

尝试使用 javac

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

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

所有复合赋值运算符都要求两个操作数都是原始类型, 除了 +=,它允许右手操作数是任何类型,如果左手 操作数是字符串类型。

对 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 中的难题

【讨论】:

好的,我看到了 JLS 的变化,但是数组访问的代码呢?它不应该按照最新的 JLS 工作,不是吗? @Malcom 我没有看到任何矛盾。如果“如果 T 是引用类型,那么它必须是字符串”意味着它 T 必须是字符串或者 T 被作为字符串处理,则可以有一个参数。我想说,这是第二种选择,请参阅 JLS 5.1.11。字符串转换。和 15.18.1。 (If only one operand expression is of type String, then string conversion (§5.1.11) is performed on the other operand to produce a string at run-time.) 是的,但是如果自动应用字符串转换,为什么要写关于字符串的句子呢?此外,转换发生在执行二进制运算时,并且发生在假定检查操作数是否为字符串之后。就个人而言,我认为这是一个 JLS 错误:他们在段落开头删除了关于 String 要求的行并忘记了这部分。 您可以尝试将其作为错误发布,看看会发生什么。 也许我会这样做。好的,我想问题现在已经回答了,只是这个谜题没有答案,但现在显然无法解决。【参考方案2】:

在 Java 6 中你可以说

Object x = 1;
String i = "i";
x = x + i; // compiles
x += i; // doesn't compile in Java 6, but does in Java 7.
System.out.println(x);

为什么会这样?

同理

x[0] = x[0] + i;

使用 Java 7 编译

Object[] x = new Object();
String i = "Effective Java!";
x[0] +=  i;
System.out.println(x[0]);

打印

java.lang.Object@a62b39fEffective Java!

但不适用于 Java 6 更新 37

    Error:Error:line (25)java: src\Main.java:25: incompatible types
found   : java.lang.Object
required: java.lang.String

这是否意味着 Java 编译器偏离了 JLS?

我怀疑这意味着 Java 6 不遵循当前的 JLS。它可能确实符合旧版本。

还有可能以某种方式解决最初的难题吗?

有提示。这样编译

char ch = '0';
ch *= 1.1;

这不是

char ch = '0';
ch = ch * 1.1;

【讨论】:

在他的示例中有 (Object) += (String)。您的建议仅适用于反向:(String) += (Object)(当 Object 转换为 String 时)。 @DmitryZaitsev 它适用于 Java 7。您尝试过哪个版本的 Java? @DmitryZaitsev 这表明它可能是 Java 6 中的 JLS 错误。 相反,JDK7似乎并没有坚持JLS,因为JLS要求左边的值是String,如果它在数组中,它仍然编译运行没有问题。尽管作为一名程序员,我认为这种行为实际上比 JLS 中的更好,因为它与x + i 一致。关于原始拼图,我正在寻找+= 是非法的,+ 是合法的情况。我知道由于隐藏演员而发生的相反情况,我对此不感兴趣。【参考方案3】:

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

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

【讨论】:

您使用的是哪个版本的 Java?这在 Java 7 上编译和运行。 那么 Java 7 会忽略 JLS。 但是 JLS 是否说 x += i 不应该工作?在我看来,JLS 仅在数组中需要 String,在所有其他情况下都没有关系。 JLS 只是说first, the left-hand operand is evaluated to produce a variable 然后是the saved value of the left-hand variable and the value of the right-hand operand are used to perform the binary operation indicated by the compound assignment operator,这与x + i 没有什么不同。没有说左手表达式是字符串。

以上是关于如何使 E1 += E2 非法而 E1 = E1 + E2 是合法的?的主要内容,如果未能解决你的问题,请参考以下文章

EXCL 隔行公式

什么是RSA算法,有公钥和私钥对?他的处理过程是这样的?

excel 如何在时间数据出现间断的数据行前面 插入空白行

如何基于单个单元格值格式化整个列?

LaTeX中如何定义使\section编译之后显示第一章,第二章这种

如何更好地识别 SQL 中指定的开始和结束端点内的表冲突?