如何使 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)。这个谜题的本质是
为变量
x
和i
提供声明,这样这是一个法律声明: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 是合法的?的主要内容,如果未能解决你的问题,请参考以下文章