三元运算符如何评估结果数据类型?
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)
,它将被选中,因为 Serializable
比 Object
更具体。
- 如果我们添加两个重载
foo(Serializable)
和 foo(Comparable<?>)
,我们将得到一个歧义错误。
给出这段代码
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)
,它将被选中,因为Serializable
比Object
更具体。 - 如果我们添加两个重载
foo(Serializable)
和foo(Comparable<?>)
,我们将得到一个歧义错误。