条件表达式 "? :" 编译尽管分支返回不同类型
conditional expression "? :" compiles despite branches returning different types
我已经开始学习 java,但我遇到了以下条件表达式:
((1<2)?5:(3<4))
在我找到这个例子的书中,它说这是一个语法错误,因为它不能将数值转换为布尔值。
在相同的页面之后,有一个包含不同练习的测试,包括这个 one.After 在 eclipse 中编写和编译它给了我输出 5。
为什么?我读过一些关于此运算符的内容,它清楚地表明两个表达式都必须是布尔值或算术,所以这是 eclipse 的问题吗?
当他们谈到类似这样的编译器错误情况时
int a= ((1<2)?5:(3<4));
但作为 (3<4) returns 布尔值,它不能分配给整型变量
但是当你简单地做System.out.println(((1<2)?5:(3<4)));
时,它不必将值赋给任何变量,它会使用方法System.out.println(Object)
打印输出值
如果你稍微改变一下表达式并执行:- System.out.println(((1>2)?5:(3<4)))
那么它将打印 true
作为输出
条件运算符有三个操作数 - 第一个是条件,第二个和第三个是分开的 "branches"。评估哪个分支取决于条件评估为真还是假。
在你的情况下,你有:
- 条件:
1 < 2
- 分支 1:
5
- 分支 2:
3 < 4
现在的问题是这里的两个分支是不同的类型,所以表达式没有有用的原始结果类型。它(令人惊讶的是,IMO)有效,整体表达式类型为 Object
- 基本上,两个分支都涉及装箱。所以这个:
Object o = 1 < 2 ? 5 : 3 < 4;
相当于:
Object o = 1 < 2 ? (Integer) 5 : (Boolean) (3 < 4);
在 JLS 15.25 中指定 - 您的情况显示在 "Table 15.25-B. Conditional expression type (Primitive 3rd operand, Part II)" 中,这表明当第三个操作数的类型为 boolean
而第二个操作数的类型为 int
,结果是 lub(Integer,Boolean)
- 基本上是 Object
.
这样就好了:
int x = 1 < 2 ? 5 : 4; // Both branches are of type int
这也是:
// Very confusing with all these conditions, but valid!
boolean y = 1 < 2 ? 5 < 6 : 3 < 4; // Both branches are of type boolean
...但是您当前的情况有一个 int
类型的分支和一个 boolean
.
类型的分支
?
是 三元运算符 ,详见 JSL (§15.25):
variable = condition ? value if true : value if false;
示例
String isFalse = 3 < 4 ? "true" : "false"; // false
String isTrue = 3 == 3 ? "true" : "false"; // true
条件表达式的类型判断如下:
如果第二个和第三个操作数的类型相同(可能是null类型),那么就是条件表达式的类型。
如果第二个和第三个操作数之一是基本类型T,另一个的类型是对T应用装箱转换(§5.1.7)的结果,则条件表达式是 T.
如果第二个和第三个操作数其中一个是空类型,另一个是引用类型,那么条件表达式的类型就是那个引用类型。
否则,如果第二个和第三个操作数具有可转换(§5.1.8)为数字类型的类型,则有几种情况:
现在,了解这一点并翻译您的表达方式:
((1<2)?5:(3<4))
你得到 1<2=false
和 3<4=false
false ? 5 : false
因此,如果您尝试分配一个变量
boolean var = false ? 5 : false
int var = false ? 5 : false
您将遇到 语法错误,因为它无法将数值转换为布尔值:
Type mismatch: cannot convert from Object&Comparable<?>&Serializable to int<br>
↑ (3<4) ↑ variable assigned
这个三元运算符唯一的"problem"是它return不同的类型。
让我们在伪代码中用 if
条件解包:
((1<2)?5:(3<4))
if (1 < 2) {
// returns int
return 5;
}
else {
// either returns boolean (true)
if (3 < 4) {
return true;
}
else {
return false;
}
}
所以表达式在某些条件下是有效的。
例如,Object o = ((1<2)?5:(3<4));
可以正常编译(在 3 < 4
部分有警告),因为 Object
可以从原语 boolean
和 int
。
无论 x
和 y
的值是什么,下面的代码都能正确编译和运行。您得到数字 5 或布尔值 true。没有问题,因为结果没有分配到任何地方(或者结果是 Object
传递给 println(Object o)
)。
int x = 1;
int y = 2;
System.out.println(x<y ? 5 : 3<4);
问题来自作业。
int x = 1;
int y = 2;
int a = x<y ? 5 : 3<4; // Won't compile without a cast to int, if x > y, results in a `ClassCastException`.
因此,虽然结果可能有不同的类型,但这不一定是个好主意。它们都需要可分配给结果,因此无论从任何一个语句返回什么,下面的语句总是有效的。
Object o = x < y ? "String object" : new ArrayList(); // Works since the return type is compatible
如果您将它传递给 System.out.println()
它不会抱怨,因为所有值都已转换为字符串。
所以,
System.out.println((1<2) ? 5 : (3<4));
//Will print 5
System.out.println((3<2) ? 5 : (3<4));
//Will print true
但是,
int i = ((3<2) ? 5 : (3<4));
或
boolean b = ((3<2) ? 5 : (3<4));
会导致编译错误。
由于在 a?b:c
表达式中,b
和 c
的类型可能不同,Java 编译器必须尝试推断 return 表达式的类型,以找到适合两个操作数的类型。
这是通过查看类型树并找到最近的共同祖先来完成的。对于引用类型,这个最近的共同祖先在最坏的情况下是 Object
。 (1 > 2) ? new StringBuilder() : new ArrayList();
将有一个 return 类型 Object
,所以如果你想将它分配给类型 Object
的变量(或将它传递给需要参数 Object
的方法=13=]类型),没关系。否则你会得到一个编译错误,因为不能保证结果可以转换为任何其他类型。
您的场景稍微复杂一点的地方在于它具有原语 b
和 c
:int
和 boolean
。这些必须首先自动装箱到 Integer
和 Boolean
,然后上面的过程正常运行,return 类型是 Object
。但是如果你想将一个 Object
分配给一个 int
,那是行不通的,这就是为什么 int x = ((1<2)?5:(3<4));
编译失败而 Object x = ((1<2)?5:(3<4));
有效的原因。
注意:在实践中,由于可能存在多个不相关的共同祖先,所以情况会稍微复杂一些,但这不会改变基本原则。
该语句本身不会给出任何编译错误,但条件语句的结果不能分配给变量,因为如果条件为真(如当前情况),结果将为 5,这是一个整数但如果条件为假,则结果将为真,这是一个布尔值。
所以要得到编译错误,请执行以下操作,
Integer a = ((1>2)?5:(3<4));
或
Boolean a = ((1>2)?5:(3<4));
为了使代码能够编译,我们可以将结果分配给基数 class。
Serializable a = ((1>2)?5:(3<4));
或
Object a = ((1>2)?5:(3<4));
3<4 将 return 布尔值。不是整数。
1<2 ?是的,然后打印 5
但是,如果您将“<”更改为“>”,您将得到 "true".
我已经开始学习 java,但我遇到了以下条件表达式:
((1<2)?5:(3<4))
在我找到这个例子的书中,它说这是一个语法错误,因为它不能将数值转换为布尔值。 在相同的页面之后,有一个包含不同练习的测试,包括这个 one.After 在 eclipse 中编写和编译它给了我输出 5。 为什么?我读过一些关于此运算符的内容,它清楚地表明两个表达式都必须是布尔值或算术,所以这是 eclipse 的问题吗?
当他们谈到类似这样的编译器错误情况时
int a= ((1<2)?5:(3<4));
但作为 (3<4) returns 布尔值,它不能分配给整型变量
但是当你简单地做System.out.println(((1<2)?5:(3<4)));
时,它不必将值赋给任何变量,它会使用方法System.out.println(Object)
如果你稍微改变一下表达式并执行:- System.out.println(((1>2)?5:(3<4)))
那么它将打印 true
作为输出
条件运算符有三个操作数 - 第一个是条件,第二个和第三个是分开的 "branches"。评估哪个分支取决于条件评估为真还是假。
在你的情况下,你有:
- 条件:
1 < 2
- 分支 1:
5
- 分支 2:
3 < 4
现在的问题是这里的两个分支是不同的类型,所以表达式没有有用的原始结果类型。它(令人惊讶的是,IMO)有效,整体表达式类型为 Object
- 基本上,两个分支都涉及装箱。所以这个:
Object o = 1 < 2 ? 5 : 3 < 4;
相当于:
Object o = 1 < 2 ? (Integer) 5 : (Boolean) (3 < 4);
在 JLS 15.25 中指定 - 您的情况显示在 "Table 15.25-B. Conditional expression type (Primitive 3rd operand, Part II)" 中,这表明当第三个操作数的类型为 boolean
而第二个操作数的类型为 int
,结果是 lub(Integer,Boolean)
- 基本上是 Object
.
这样就好了:
int x = 1 < 2 ? 5 : 4; // Both branches are of type int
这也是:
// Very confusing with all these conditions, but valid!
boolean y = 1 < 2 ? 5 < 6 : 3 < 4; // Both branches are of type boolean
...但是您当前的情况有一个 int
类型的分支和一个 boolean
.
?
是 三元运算符 ,详见 JSL (§15.25):
variable = condition ? value if true : value if false;
示例
String isFalse = 3 < 4 ? "true" : "false"; // false
String isTrue = 3 == 3 ? "true" : "false"; // true
条件表达式的类型判断如下:
如果第二个和第三个操作数的类型相同(可能是null类型),那么就是条件表达式的类型。
如果第二个和第三个操作数之一是基本类型T,另一个的类型是对T应用装箱转换(§5.1.7)的结果,则条件表达式是 T.
如果第二个和第三个操作数其中一个是空类型,另一个是引用类型,那么条件表达式的类型就是那个引用类型。
否则,如果第二个和第三个操作数具有可转换(§5.1.8)为数字类型的类型,则有几种情况:
现在,了解这一点并翻译您的表达方式:
((1<2)?5:(3<4))
你得到 1<2=false
和 3<4=false
false ? 5 : false
因此,如果您尝试分配一个变量
boolean var = false ? 5 : false
int var = false ? 5 : false
您将遇到 语法错误,因为它无法将数值转换为布尔值:
Type mismatch: cannot convert from Object&Comparable<?>&Serializable to int<br>
↑ (3<4) ↑ variable assigned
这个三元运算符唯一的"problem"是它return不同的类型。
让我们在伪代码中用 if
条件解包:
((1<2)?5:(3<4))
if (1 < 2) {
// returns int
return 5;
}
else {
// either returns boolean (true)
if (3 < 4) {
return true;
}
else {
return false;
}
}
所以表达式在某些条件下是有效的。
例如,Object o = ((1<2)?5:(3<4));
可以正常编译(在 3 < 4
部分有警告),因为 Object
可以从原语 boolean
和 int
。
无论 x
和 y
的值是什么,下面的代码都能正确编译和运行。您得到数字 5 或布尔值 true。没有问题,因为结果没有分配到任何地方(或者结果是 Object
传递给 println(Object o)
)。
int x = 1;
int y = 2;
System.out.println(x<y ? 5 : 3<4);
问题来自作业。
int x = 1;
int y = 2;
int a = x<y ? 5 : 3<4; // Won't compile without a cast to int, if x > y, results in a `ClassCastException`.
因此,虽然结果可能有不同的类型,但这不一定是个好主意。它们都需要可分配给结果,因此无论从任何一个语句返回什么,下面的语句总是有效的。
Object o = x < y ? "String object" : new ArrayList(); // Works since the return type is compatible
如果您将它传递给 System.out.println()
它不会抱怨,因为所有值都已转换为字符串。
所以,
System.out.println((1<2) ? 5 : (3<4));
//Will print 5
System.out.println((3<2) ? 5 : (3<4));
//Will print true
但是,
int i = ((3<2) ? 5 : (3<4));
或
boolean b = ((3<2) ? 5 : (3<4));
会导致编译错误。
由于在 a?b:c
表达式中,b
和 c
的类型可能不同,Java 编译器必须尝试推断 return 表达式的类型,以找到适合两个操作数的类型。
这是通过查看类型树并找到最近的共同祖先来完成的。对于引用类型,这个最近的共同祖先在最坏的情况下是 Object
。 (1 > 2) ? new StringBuilder() : new ArrayList();
将有一个 return 类型 Object
,所以如果你想将它分配给类型 Object
的变量(或将它传递给需要参数 Object
的方法=13=]类型),没关系。否则你会得到一个编译错误,因为不能保证结果可以转换为任何其他类型。
您的场景稍微复杂一点的地方在于它具有原语 b
和 c
:int
和 boolean
。这些必须首先自动装箱到 Integer
和 Boolean
,然后上面的过程正常运行,return 类型是 Object
。但是如果你想将一个 Object
分配给一个 int
,那是行不通的,这就是为什么 int x = ((1<2)?5:(3<4));
编译失败而 Object x = ((1<2)?5:(3<4));
有效的原因。
注意:在实践中,由于可能存在多个不相关的共同祖先,所以情况会稍微复杂一些,但这不会改变基本原则。
该语句本身不会给出任何编译错误,但条件语句的结果不能分配给变量,因为如果条件为真(如当前情况),结果将为 5,这是一个整数但如果条件为假,则结果将为真,这是一个布尔值。
所以要得到编译错误,请执行以下操作,
Integer a = ((1>2)?5:(3<4));
或
Boolean a = ((1>2)?5:(3<4));
为了使代码能够编译,我们可以将结果分配给基数 class。
Serializable a = ((1>2)?5:(3<4));
或
Object a = ((1>2)?5:(3<4));
3<4 将 return 布尔值。不是整数。
1<2 ?是的,然后打印 5 但是,如果您将“<”更改为“>”,您将得到 "true".