Number n = 10;
int i = 10;
System.out.println(n == i);
基于 "You CAN box then widen"。为什么上述代码会出现编译时错误?
我猜测如果 i 首先被装箱成 Integer,然后扩展成 Number,结果将始终为 false。
有关使用 == 运算符比较原语和对象时的任何规范吗?是否总是尝试执行拆箱,必要时进行扩展?
Number n = 10;
int i = 10;
System.out.println(n == i);
根据JLS的规定, 一个装箱值和一个非装箱值之间的==
比较会导致拆箱转换,而不是相反(否则你将使用引用相等性而不是值相等性)。编译器无法解包一个普通的Number
;Number
本身不可“转换为数字类型”。
public static void main(java.lang.String[]);
Code:
0: bipush 10
2: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
5: astore_1
6: bipush 10
8: istore_2
9: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
12: aload_1
13: bipush 10
15: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
18: if_acmpne 25
21: iconst_1
22: goto 26
25: iconst_0
26: invokevirtual #4 // Method java/io/PrintStream.println:(Z)V
29: return
防止优化的版本:
public class Test
{
public static void main(String[] args)
{
Number n = new Integer(10);
compare(n);
}
public static void compare(Number n)
{
int i=10;
System.out.println(n == 10);
}
}
汇编语言;请注意,在第12行中仍然进行引用比较:
public static void main(java.lang.String[]);
Code:
0: new #2 // class java/lang/Integer
3: dup
4: bipush 10
6: invokespecial #3 // Method java/lang/Integer."<init>":(I)V
9: astore_1
10: aload_1
11: invokestatic #4 // Method compare:(Ljava/lang/Number;)V
14: return
public static void compare(java.lang.Number);
Code:
0: bipush 10
2: istore_1
3: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
6: aload_0
7: bipush 10
9: invokestatic #6 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
12: if_acmpne 19
15: iconst_1
16: goto 20
19: iconst_0
20: invokevirtual #7 // Method java/io/PrintStream.println:(Z)V
23: return
int
进行了处理,这可能是程序员想要的。Eclipse 编译器是正确的。 - chrylis -cautiouslyoptimistic-javac
编译器,则可以编译并打印true
。Number n = 10
将被转换为Number n = Integer.valueOf(10);
稍后,编译器将创建n == Integer.valueOf(10)
(自动装箱整数值)。Integer.valueOf()
对于小整数数值保持内部缓存,并总是返回它们的相同实例。Integer.valueOf(10) == Integer.valueOf(10)
但这只是实现的副作用,你不应该依赖它。
字节码:
public static void main(java.lang.String[]);
Code:
0: bipush 10
2: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
5: astore_1
6: bipush 10
8: istore_2
9: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
12: aload_1
13: iload_2
14: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
17: if_acmpne 24
20: iconst_1
21: goto 25
24: iconst_0
25: invokevirtual #4 // Method java/io/PrintStream.println:(Z)V
28: return
Oracle的JDK 7中的编译器允许您通过自动装箱原始类型来编译代码,但这违反了Java语言规范。
根据JLS第15.21节,有三种变体的等号运算符:
15.21.1. 数值等式运算符 == 和 !=
描述了"如果等式运算符的操作数都是数值类型,或者一个是数值类型而另一个可以转换为数值类型(§5.1.8),则该运算符返回 true"。在这种情况下,一个操作数具有数值类型,但另一个操作数不可转换为数值类型(拆箱)。
15.21.2. 布尔等式运算符 == 和 !=
描述了"如果等式运算符的操作数都是布尔类型,或者一个操作数是布尔类型而另一个是Boolean类型,则该运算符返回true"。这也不是这种情况。
15.21.3. 引用等式运算符 == 和 !=
描述了等号运算符“如果等号运算符的操作数都是引用类型或空类型,则为真”。但事实并非如此。