我正在玩三元运算符并注意到一些奇怪的东西。我的代码如下:
class Main {
static void foo(int a){
System.out.println("int");
}
static void foo(String a){
System.out.println("String");
}
static void foo(Object a){
System.out.println("object");
}
public static void main(String[] args) {
foo(2==3 ? 0xF00:"bar");
System.out.println((2==3 ? 0xF00:"bar").getClass().getName());
}
}
结果是
对象
java.lang.String中
第一行结果显示该指令使用object参数传递给 foo 方法。
指令本身导致字符串的第二行。
问题:
为什么结果是String编译器决定使用Object?
这是因为类型模糊吗?
如果是,那么为什么要让类名返回 java.lang.String ?
答案 0 :(得分:8)
在Java中,您有编译时类型信息,并且您有运行时类型信息。编译时间类型信息是编译器可以通过查看它而不执行它来推断值或表达式的类型。当编译器看到表达式
时2 == 3 ? 0xF00 : "bar"
它不知道2 == 3
是真还是假,因为它不执行代码。所有它知道的结果可能是Integer
或String
。因此,当需要选择要调用的foo
方法时,它会选择接受Object
的方法,因为这是它知道在两个方案中都有效的唯一方法。
但是,当代码实际运行时,2 == 3
将为false,结果将为String
,其getClass()
方法将返回String.class
。这就是你需要注意的:getClass()
不返回变量在编译时具有的类型,但它返回变量在运行时保存的对象的实际类型。即。
Object o = "Hello!";
System.out.println(o.getClass());
将打印java.lang.String
,因为即使编译器是Object
,在运行时它实际上是String
。
答案 1 :(得分:3)
在编译阶段,编译器注意到2 == 3 ? 0xF00 : "bar"
的结果可能是int或String。为了与两者兼容,它决定拨打foo(Object a)
。
在运行时,2 == 3 ? 0xF00 : "bar"
的结果是字符串bar
。
答案 2 :(得分:2)
如果对阅读不感兴趣,可以跳到摘要。
请在此处参阅JavaDoc: https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.25
根据15.25.3。参考条件表达式它说:
条件表达式的类型是 应用捕获转换的结果
对于捕获转换:https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.1.10
摘要:
对于类型确定,使用捕获转换,其中对于您的示例,int首先被装箱为Integer,然后获取最接近的Integer和String的公共超类,即Object类。因此条件表达式的类型是Object,因此调用Object作为参数的方法。
现在对于第二部分,首先计算条件运算符,然后将其取消装箱,然后计算.getClass()。所以它打印java.lang.String。
此处还记录了这些内容:https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.25
15.25岁以下。条件运算符? :
在运行时,条件的第一个操作数表达式 首先评估表达式。如有必要,将取消装箱转换 对结果进行了表达。