提问人:Lion 提问时间:11/12/2011 最后编辑:RaedwaldLion 更新时间:6/12/2019 访问量:17551
使用三元运算符允许返回 null 作为 int,但不允许 if 语句
Returning null as an int permitted with ternary operator but not if statement
问:
让我们看一下以下代码片段中的简单 Java 代码:
public class Main {
private int temp() {
return true ? null : 0;
// No compiler error - the compiler allows a return value of null
// in a method signature that returns an int.
}
private int same() {
if (true) {
return null;
// The same is not possible with if,
// and causes a compile-time error - incompatible types.
} else {
return 0;
}
}
public static void main(String[] args) {
Main m = new Main();
System.out.println(m.temp());
System.out.println(m.same());
}
}
在这个最简单的 Java 代码中,即使函数的返回类型为 ,该方法也不会发出编译器错误,并且我们尝试返回值(通过语句)。编译时,这显然会导致 运行时异常 。temp()
int
null
return true ? null : 0;
NullPointerException
但是,如果我们用语句表示三元运算符(如方法中所示),这似乎是错误的,这确实会发出编译时错误!为什么?if
same()
答:
我认为,Java 编译器解释为一个表达式,可以隐式转换为 ,可能给出 .true ? null : 0
Integer
int
NullPointerException
对于第二种情况,表达式属于特殊的 null 类型 see,因此代码会使类型不匹配。null
return null
评论
true ? null : 0
Integer
0
编译器将 解释为 对 的空引用,应用条件运算符的自动装箱/取消装箱规则(如 Java 语言规范 15.25 中所述),然后愉快地继续前进。这将在运行时生成一个,您可以通过尝试来确认。null
Integer
NullPointerException
评论
capture conversion
lub(T1,T2)
lub(T1,T2)
null
null
null
Integer
null
Integer
实际上,这一切都在 Java 语言规范中进行了解释。
条件表达式的类型确定如下:
- 如果第二个和第三个操作数具有相同的类型(可能是 null 类型),则这就是条件表达式的类型。
因此,your 中的 “null” 得到一个 int 类型,然后被自动装箱为 Integer。(true ? null : 0)
尝试这样的东西来验证这一点,你会得到编译器错误。(true ? null : null)
评论
int
null
Integer
new Integer(null);
NumberFormatException
在语句的情况下,引用不被视为引用,因为它没有参与强制将其解释为引用的表达。因此,该错误很容易在编译时捕获,因为它更明显地是类型错误。if
null
Integer
至于条件运算符,Java 语言规范 §15.25 “条件运算符 ” 在如何应用类型转换的规则中很好地回答了这个问题:? :
- 如果第二个和第三个操作数具有相同的类型(可能是 null type),则这是条件表达式的类型。
不适用,因为null
不是int
。
- 如果第二个和第三个操作数之一的类型为 boolean 且 other 的类型为 Boolean,则条件表达式的类型为 Boolean。
不适用,因为null
和int
都不是布尔
值或布尔值
。
- 如果第二个和第三个操作数之一为 null 类型,并且 other 是引用类型,那么条件表达式的类型是 引用类型。
不适用,因为 null 是null
类型,但int
不是引用类型。
- 否则,如果第二个和第三个操作数具有可转换的类型 (§5.1.8) 到数值类型,则有几种情况:[...]
适用:null
被视为可转换为数值类型,并在 §5.1.8“取消装箱转换”中定义以引发NullPointerException
。
评论
0
Integer
Integer
int
Integer
null
null
null
int
throw new NullPointerException()
首先要记住的是,Java 三元运算符有一个“类型”,无论第二个或第三个参数的实际/实际类型是什么,编译器都会确定和考虑这一点。根据几个因素,三元运算符类型以不同的方式确定,如 Java 语言规范 15.26 中所示
在上面的问题中,我们应该考虑最后一种情况:
否则,第二个和第三个操作数分别属于 S1 和 S2 类型。设 T1 是将装箱转换应用于 S1 所生成的类型,并设 T2 是将装箱转换应用于 S2 所生成的类型。条件表达式的类型是将捕获转换 (§5.1.10) 应用于 lub(T1, T2) (§15.12.2.7) 的结果。
这是迄今为止最复杂的情况,一旦您查看了应用捕获转换 (§5.1.10),尤其是在 lub(T1, T2) 中。
用通俗易懂的英语和极端简化之后,我们可以将该过程描述为计算第二个和第三个参数的“最小公超类”(是的,想想 LCM)。这将为我们提供三元运算符“类型”。同样,我刚才所说的是一种极端的简化(考虑实现多个通用接口的类)。
例如,如果您尝试以下操作:
long millis = System.currentTimeMillis();
return(true ? new java.sql.Timestamp(millis) : new java.sql.Time(millis));
您会注意到,条件表达式的结果类型是因为它是 / 对的“最不常见超类”。java.util.Date
Timestamp
Time
由于可以自动装箱到任何内容,因此“最小公共超类”是类,这将是上面条件表达式(三元运算符)的返回类型。然后,返回值将是类型的 null 指针,这是三元运算符返回的内容。null
Integer
Integer
在运行时,当 Java 虚拟机拆箱时,会抛出 a。发生这种情况是因为JVM尝试调用函数,其中是自动装箱的结果。Integer
NullPointerException
null.intValue()
null
在我看来(由于我的观点不在 Java 语言规范中,所以很多人无论如何都会发现它是错误的),编译器在评估您问题中的表达式方面做得很差。假设您编写了编译器,编译器应立即确定将返回第一个参数 -- 并将生成编译器错误。这有点类似于您编写时,编译器抱怨循环下面的代码并用 .true ? param1 : param2
null
while(true){} etc...
Unreachable Statements
你的第二个案例很简单,这个答案已经太长了...... ;)
校正:
经过另一次分析,我认为我说一个值可以装箱/自动装箱到任何东西是错误的。谈到 Integer 类,显式装箱包括调用构造函数或(我在某处找到了这个版本)。前者会抛出一个(这不会发生),而第二个则没有意义,因为不能......null
new Integer(...)
Integer.valueOf(int i);
NumberFormatException
int
null
评论
null
null
1
false
true
null
int
null
实际上,在第一种情况下,表达式可以被计算,因为编译器知道,它必须被计算为 ,但是在第二种情况下,无法确定返回值 () 的类型,因此无法编译它。如果将其转换为 ,则代码将编译。Integer
null
Integer
private int temp() {
if (true) {
Integer x = null;
return x;// since that is fine because of unboxing then the returned value could be null
//in other words I can say x could be null or new Integer(intValue) or a intValue
}
return (true ? null : 0); //this will be prefectly legal null would be refrence to Integer. The concept is one the returned
//value can be Integer
// then null is accepted to be a variable (-refrence variable-) of Integer
}
这个怎么样:
public class ConditionalExpressionType {
public static void main(String[] args) {
String s = "";
s += (true ? 1 : "") instanceof Integer;
System.out.println(s);
String t = "";
t += (!true ? 1 : "") instanceof String;
System.out.println(t);
}
}
输出为 true,true。
Eclipse 将条件表达式中的 1 颜色编码为自动装箱。
我的猜测是编译器将表达式的返回类型视为 Object。
评论
int foo = (true ? null : 0)
new Integer(null)
null
Integer
Integer foo() { return "1"; }