我在 Java Puzzlers VI 中发现了一个错误 - 有人能解释一下吗?
看看 Josh Bloch 和 William Pugh 制作的Java 谜题视频,时间索引 0:25:00-0:33:00 左右。
一位发言者说,如果您使用小写的 boolean
而不是 Boolean
,那么 LIVING
将被视为真正的“编译时间常量”,并且什么时候初始化就不再重要了。
好吧,这一切都很好,但是,看看当您恢复静态 init 和构造函数之间的原始顺序时会发生什么,然后通过简单的“提取方法”操作来跟进。这两个程序打印不同的输出:
public class Elvis {
private static final Elvis ELVIS = new Elvis();
private Elvis () {}
private static final boolean LIVING = true;
private final boolean alive = LIVING;
private final boolean lives () {return alive;}
public static void main(String[] args) {
System.out.println(ELVIS.lives()); // prints true
}
}
使用重构的 returnTrue() 方法
public class Elvis {
private static final Elvis ELVIS = new Elvis();
private Elvis () {}
private static final boolean LIVING = returnTrue();
private static boolean returnTrue() {
return true;
}
private final boolean alive = LIVING;
private final boolean lives () {return alive;}
public static void main(String[] args) {
System.out.println(ELVIS.lives()); // prints false
}
}
为什么在这种情况下提取 returnTrue() 方法会改变程序输出?
Take a look at this java puzzles vid by Josh Bloch and William Pugh, around time index 0:25:00-0:33:00.
One of the speakers says that if you use lowercase boolean
instead of Boolean
, then LIVING
will be treated as a true "compile time constant", and it no longer matters when it is initialized.
Well, this is all fine and dandy, but, take a look at what happens when you revert to the original order between the static init and the constructor, and then follow it up by a simple "Extract Method" operation. These two programs print different outputs:
public class Elvis {
private static final Elvis ELVIS = new Elvis();
private Elvis () {}
private static final boolean LIVING = true;
private final boolean alive = LIVING;
private final boolean lives () {return alive;}
public static void main(String[] args) {
System.out.println(ELVIS.lives()); // prints true
}
}
And with the refactored returnTrue()
method
public class Elvis {
private static final Elvis ELVIS = new Elvis();
private Elvis () {}
private static final boolean LIVING = returnTrue();
private static boolean returnTrue() {
return true;
}
private final boolean alive = LIVING;
private final boolean lives () {return alive;}
public static void main(String[] args) {
System.out.println(ELVIS.lives()); // prints false
}
}
Why does extracting the returnTrue() method change the program output in this case?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您所观察到的行为的关键是“常量变量”的概念。这种矛盾是在 JLS 4.12.4 作为原始类型或 String 类型的变量,它是最终的并使用编译时常量表达式进行初始化。在 JLS 13.1 中,它说引用常量字段在编译时解析为它们表示的常量值(即,它们是内联的)。视频中的谜题依赖于这样一个事实:布尔值既不是基元也不是字符串。您的变体依赖于这样一个事实:在表达式中调用方法 (returnTrue) 可防止其成为编译时常量表达式。无论哪种方式,LIVING 都不是常量变量,程序会显示违反直觉的行为。
Java Puzzlers(《Class Warfare》)中的 Puzzle 93 与之相关,甚至更令人惊讶。
The key to the behavior that you're observing is notion of "constant variable." This oxymoron is defined in JLS 4.12.4 as a variable of primitive type or type String that is final and initialized with a compile-time constant expression. In JLS 13.1, it says that references to constant fields are resolved at compile time to the constant values they denote (i.e., they're inlined). The puzzle in the video relies on the fact that a Boolean is neither a primitive nor a String. Your variant relies on the fact that invoking a method (returnTrue) in an expression prevents it from being a compile-time constant expression. Either way, LIVING is not a constant variable and the program displays the counterintuitive behavior.
Puzzle 93 in Java Puzzlers ("Class Warfare") is related, and even more surprising.
在第二种情况下,
LIVING
由运行时表达式初始化,因此它不再是编译时常量,并且在ELVIS 运行时其值为
已构建。false
In the second case
LIVING
is initialized by the runtime expression, therefore it's not a compile-time constant any more, and its value isfalse
at the time whenELVIS
is constructed.