具有不同类型表达式的三元运算符

Ternary operator with different types of expressions

我在玩三元运算符时发现了一些奇怪的东西。我有以下代码:

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());
  }
}

结果是

object

java.lang.String

结果的第一行显示该指令传递给带有对象参数的 foo 方法。

指令本身导致 String.

的第二行

问题:

  1. 为什么如果结果是字符串编译器决定使用对象?

  2. 这是因为类型歧义吗?

  3. 如果是那么为什么要返回 class 名称 java.lang.String?

在编译阶段,编译器注意到2 == 3 ? 0xF00 : "bar"的结果可能是int或String。为了与两者兼容,它决定调用 foo(Object a).

在运行时,2 == 3 ? 0xF00 : "bar" 的结果是字符串 bar

在Java中,你有编译时间类型信息和运行时间类型信息。编译时类型信息是编译器仅通过查看而不是执行就可以推断出值或表达式的类型的信息。当编译器看到表达式

2 == 3 ? 0xF00 : "bar"

它不知道2 == 3是真还是假,因为它不执行代码。所以它只知道结果可以是 IntegerString。因此,当需要选择调用哪个 foo 方法时,它会选择接受 Object 的方法,因为它知道这是唯一一个在两种情况下都可以工作的方法。

但是当代码实际是运行ning时,2 == 3会是false,结果会是一个String,其getClass()方法会return String.class。这就是您需要注意的:getClass() 不是 return 变量在编译时的类型,而是 return 变量持有的对象的实际类型运行 时间。即

Object o = "Hello!";
System.out.println(o.getClass());

将打印 java.lang.String,因为即使对编译器来说它是 Object,在 运行 时它实际上是 String.

不感兴趣的可以跳到摘要。

在此处参考 JavaDoc:https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.25

在 15.25.3 下。参考条件表达式它说:

The type of the conditional expression is the result of applying capture conversion

捕获转换:https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.1.10

总结:

对于类型确定,使用捕获转换,其中对于您的示例,首先将 int 装箱为 Integer,然后获取 Integer 和 String 最接近的公共 super class,即 Object class。所以条件表达式的类型是对象,所以调用以对象为参数的方法。

现在对于第二部分,首先评估条件运算符,然后取消装箱,然后评估 .getClass()。所以它打印 java.lang.String.

这也记录在这里:https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.25

低于 15.25。条件运算符? :

At run time, the first operand expression of the conditional expression is evaluated first. If necessary, unboxing conversion is performed on the result.