【问题标题】:How does the ternary operator evaluate the resulting datatype?三元运算符如何评估结果数据类型?
【发布时间】:2026-02-22 05:50:02
【问题描述】:

鉴于这段代码

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

这是我得到的输出:

整数 细绳 目的 目的

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

编辑:

让我困惑的是断言

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

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

System.out.println() 并不关心语句是完全不同的类型,因为它可以将两者都转换为 String

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

【问题讨论】:

  • 重载分辨率总是在编译时确定。
  • @Radiodef OP 询问为什么三元运算符的结果是 Object 而不是 intString
  • @LuiggiMendoza 那么为什么它应该是intString 而不是Object? OP 表示他们认为应该在运行时评估该类型,但事实并非如此。要使结果为intString,因此必须在编译时评估条件,并且在大多数情况下这是不可能的。考虑到我之前的评论,程序的行为应该是合理的。
  • 是的,你说得对,这本书误导了你。
  • @LouisWasserman 这不是唯一的误导性信息......另一个,我会把它点燃!而且是甲骨文自己建议的……

标签: java type-conversion ternary-operator


【解决方案1】:

三元组的两个替代项必须是同一类型。 Integer 和 String 唯一常见的类型是 Object,因此操作数都被强制转换为 Object,并且三元的类型在编译时确定为 Object。

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

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

【讨论】:

  • 这里的重点是重载方法的解析发生在编译时。
  • 嗯,条件表达式的类型是根据第三个和第二个操作数的类型来确定的,实际上可以不同
【解决方案2】:

三元表达式的返回类型不是在运行时计算的吗?

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

表达式的类型受JLS 15.25 规则的约束。在这两种情况下,第三个操作数都是String 类型,导致这是一个引用条件表达式,因此应用了表15.25-E,结果为lub(Integer,Object)lub 部分指的是 JLS 4.10.4,这相当令人困惑 - 这里的类型与 Object 不同完全,但在大多数情况下可以这样认为。

【讨论】:

  • +1 表示“如果您没有将结果传递给方法,而是尝试将其分配给变量,您期望会发生什么?”
  • 嗯,这是有道理的......我试图通过阅读那段来找到答案,但它仍然让我有点困惑
  • lub 的规则非常密集。 IMO 开头的句子虽然解释得很清楚。 '一组引用类型的最小上限或“lub”是一个共享超类型,它比任何其他共享超类型都更具体。'
  • 说实话,我不会声明任何变量,因为我从来没有使用过那种形式。但是认证,你知道...
  • @Radiodef:我一直在寻找定义,但找不到...它在哪里? (以前看过,现在找不到了……)
【解决方案3】:

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

引用类型的三元条件的结果最终为lub(trueType, falseType),因此在本例中为lub(Integer, String)lub is:

一组引用类型的最小上限或“lub”是一个共享超类型,它比任何其他共享超类型都更具体 [...]。

为了理解lub,我们可以对简单类型的算法做一种“临时”版本,如下所示。

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

+------------------------+------------------------+
|      Integer           |       String           |
+------------------------+---------+--------------+
| classes |  interfaces  | classes |  interfaces  |
+------------------------+---------+--------------+
| Object  | Serializable | Object  | Serializable |
| Number  | Comparable   | String  | Comparable   |
| Integer |              |         | CharSequence |
+---------+--------------+---------+--------------+

(实际的算法会将类和接口集中在一起作为“超类型”。)

现在:

  • 删除所有超类,从底部开始,直到遇到常见的超类。
  • 删除所有未共享的接口。
+------------------------+------------------------+
|      Integer           |       String           |
+------------------------+---------+--------------+
| classes |  interfaces  | classes |  interfaces  |
+------------------------+---------+--------------+
| Object  | Serializable | Object  | Serializable |
| Number  | Comparable   | String  | Comparable   |
| Integer |              |         | CharSequence |
+---------+--------------+---------+--------------+

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&lt;?&gt;),我们会得到一个歧义错误。

【讨论】: