条件表达式 "? :" 编译尽管分支返回不同类型

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=false3<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 可以从原语 booleanint

无论 xy 的值是什么,下面的代码都能正确编译和运行。您得到数字 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 表达式中,bc 的类型可能不同,Java 编译器必须尝试推断 return 表达式的类型,以找到适合两个操作数的类型。

这是通过查看类型树并找到最近的共同祖先来完成的。对于引用类型,这个最近的共同祖先在最坏的情况下是 Object(1 > 2) ? new StringBuilder() : new ArrayList(); 将有一个 return 类型 Object,所以如果你想将它分配给类型 Object 的变量(或将它传递给需要参数 Object 的方法=13=]类型),没关系。否则你会得到一个编译错误,因为不能保证结果可以转换为任何其他类型。

您的场景稍微复杂一点的地方在于它具有原语 bcintboolean。这些必须首先自动装箱到 IntegerBoolean,然后上面的过程正常运行,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".