三元运算符如何评估结果数据类型?

How does the ternary operator evaluate the resulting datatype?

给出这段代码

public class Main {

    public static void main(String[] args) {
        foo(1);
        foo("1");
        foo(true?1:"1");
        foo(false?1:"1");
    }

    static void foo(int i){System.out.println("int");}
    static void foo(String s){System.out.println("String");}
    static void foo(Object o){System.out.println("Object");}
}

这是我得到的输出:

int
String
Object
Object

我不明白为什么在最后两种情况下调用 foo(Object o),而不是 foo(int i)foo(String s)。 return 类型不是在运行时评估的三元表达式吗?

编辑:

令我困惑的是

System.out.println((y>5) ? 21 : "Zebra");

编译因为(OCA 学习指南 - Sybex):

The System.out.println() does not care that the statements are completely differnt types, because it can convert both to String

而重点是 println 被重载以接受 Object 类型作为输入。相当误导,恕我直言。

三元组的两个备选方案必须属于同一类型。 Integer 和 String 唯一常见的类型是 Object,因此操作数都被强制转换为 Object,并且三元类型在 compile 时确定为 Object。

然后编译器静态绑定到带有 Object 参数的方法。

重要的是逻辑上三元的结果在编译时是可确定的——编译器不是这样工作的。它处理表达式和类型。它必须首先解析三元表达式的 type,而不是它的 value,为此它必须首先为操作数找到一个通用类型。

Isn't the return type for a ternary expression evaluated at runtime?

不,绝对不是。这与 Java 的工作方式非常相反,重载解析等总是在编译时执行。如果您没有将结果传递给方法,而是试图将其分配给变量,您会期望发生什么?您会声明哪种变量?

表达式的类型受 JLS 15.25. In both your cases, the third operand is of type String, leading to this being a reference conditional expression, so table 15.25-E is applied, with a result of lub(Integer,Object). The lub part refers to JLS 4.10.4 规则的约束,这相当令人困惑 - 这里的类型 Object 不完全相同 ],但大多数情况下可以这样认为。

我想这是对其他答案的补充。 (或者,特别是对于任何想要理解迂腐答案的人。)

引用类型的三元条件的结果最终为 lub(trueType, falseType),因此在这种特殊情况下,结果为 lub(Integer, String)lub is:

The least upper bound, or "lub", of a set of reference types is a shared supertype that is more specific than any other shared supertype [...].

为了理解 lub,我们可以为简单类型做一个 "scratch" 版本的算法,如下所示。

首先,为每种类型制作一个table。在一栏中,写下超类层次结构,在另一栏中,写下所有实现的接口:

+------------------------+------------------------+
|      <strong>Integer</strong>           |       <strong>String</strong>           |
+------------------------+---------+--------------+
| classes |  interfaces  | classes |  interfaces  |
+------------------------+---------+--------------+
| Object  | Serializable | Object  | Serializable |
| Number  | Comparable   | String  | Comparable   |
| Integer |              |         | CharSequence |
+---------+--------------+---------+--------------+

(实际算法会将 类 和接口合并为 "supertypes"。)

现在:

  • 划掉所有的超类类,从底部开始,直到遇到公共的超类。
  • 划掉所有不共享的接口。
+------------------------+------------------------+
|      <strong>Integer</strong>           |       <strong>String</strong>           |
+------------------------+---------+--------------+
| classes |  interfaces  | classes |  interfaces  |
+------------------------+---------+--------------+
| Object  | Serializable | Object  | Serializable |
| <strike>Number</strike>  | Comparable   | <strike>String</strike>  | Comparable   |
| <strike>Integer</strike> |              |         | <strike>CharSequence</strike> |
+---------+--------------+---------+--------------+

lub 现在是该公共超类和共享接口给出的交集类型:

lub(Integer, String) =
    Object & Serializable & Comparable

除了lub这里有点复杂因为Comparable。从技术上讲,lub(Integer, String) 产生无限类型,因为我们需要为 Comparable:

的类型参数再次(无限地)执行 lub(Integer, String)
lub(Integer, String) =
    Object & Serializable & Comparable<? extends lub(Integer, String)>

条件 (boolean ? Integer : String) 的类型就是将捕获转换应用于 lub(Integer, String) 的结果类型(哎呀!)。

选择 foo(Object) 重载是因为它最适用于此类型。

其他一些有趣的事情:

  • 如果我们添加重载 foo(Serializable),它将被选中,因为 SerializableObject 更具体。
  • 如果我们添加两个重载 foo(Serializable)foo(Comparable<?>),我们将得到一个歧义错误。