Java - if-else 和三元运算符之间的不同行为
Java - different behaviours between if-else and ternary operator
最近我发现 if-else 和三元运算符之间存在一个奇怪的行为差异。
我将使用下面的单元测试代码来说明差异。
public class SomeTest {
@Test
void testWithTernary_resultIsLong() {
final SomeClass someClass = new SomeClass();
assertTrue(someClass.getNumberWithTernary() instanceof Long);
assertFalse(someClass.getNumberWithTernary() instanceof Integer);
}
@Test
void testWithIfElse_resultIsInteger() {
final SomeClass someClass = new SomeClass();
assertTrue(someClass.getNumberWithIfElse() instanceof Integer);
assertFalse(someClass.getNumberWithIfElse() instanceof Long);
}
private static class SomeClass {
public Object getNumberWithTernary() {
final long l = this.getLong();
return (l >= Integer.MIN_VALUE && l <= Integer.MAX_VALUE) ? Math.toIntExact(l) : l;
}
private long getLong() {
return 10L;
}
public Object getNumberWithIfElse() {
final long l = this.getLong();
if (l >= Integer.MIN_VALUE && l <= Integer.MAX_VALUE) {
return Math.toIntExact(l);
} else {
return l;
}
}
}
}
以上两个测试都成功。
上面声明的 class 是我在工作中遇到的代码的过度简化。
主要方法是 returning 一个 Object
,该方法需要 return 一个整数,只要有可能(即在整数范围内),以便于上游使用。
我最初使用的是三元运算方式,没想到效果出乎意料,直到做了冒烟测试。
谁能解释为什么 if-else 和三元运算符的行为在这种情况下不同?
让我们分析一下你的三元表达式(...) ? Math.toIntExact(l) : l
条件无关紧要。第二个操作数("then" 部分)的类型为 int
。第三个运算符("else" 部分)的类型为 long
.
您可以阅读 Java 语言规范 15.25。条件运算符? : 以获得精确的规则。但是基本上所有的数字操作数都强制转换为"widest"类型,因为表达式结果必须是单一类型,不能return"either long
or int
",Java类型系统不强大足够了(有些语言实际上可以做到这一点)。
所以编译器基本上将该表达式重写为以下表达式:
(...) ? ((long) Math.toIntExact(l)) : l
并将long
转换为Object
,编译器插入自动装箱调用:
Long.valueOf((...) ? ((long) Math.toIntExact(l)) : l)
这就是对为什么您会看到这种行为的粗略解释。
普通的 if
语句不能 return 值,所以它只是 return 你在 return
语句中指定的任何值,没有进一步的数字提升。
最近我发现 if-else 和三元运算符之间存在一个奇怪的行为差异。
我将使用下面的单元测试代码来说明差异。
public class SomeTest {
@Test
void testWithTernary_resultIsLong() {
final SomeClass someClass = new SomeClass();
assertTrue(someClass.getNumberWithTernary() instanceof Long);
assertFalse(someClass.getNumberWithTernary() instanceof Integer);
}
@Test
void testWithIfElse_resultIsInteger() {
final SomeClass someClass = new SomeClass();
assertTrue(someClass.getNumberWithIfElse() instanceof Integer);
assertFalse(someClass.getNumberWithIfElse() instanceof Long);
}
private static class SomeClass {
public Object getNumberWithTernary() {
final long l = this.getLong();
return (l >= Integer.MIN_VALUE && l <= Integer.MAX_VALUE) ? Math.toIntExact(l) : l;
}
private long getLong() {
return 10L;
}
public Object getNumberWithIfElse() {
final long l = this.getLong();
if (l >= Integer.MIN_VALUE && l <= Integer.MAX_VALUE) {
return Math.toIntExact(l);
} else {
return l;
}
}
}
}
以上两个测试都成功。
上面声明的 class 是我在工作中遇到的代码的过度简化。
主要方法是 returning 一个 Object
,该方法需要 return 一个整数,只要有可能(即在整数范围内),以便于上游使用。
我最初使用的是三元运算方式,没想到效果出乎意料,直到做了冒烟测试。
谁能解释为什么 if-else 和三元运算符的行为在这种情况下不同?
让我们分析一下你的三元表达式(...) ? Math.toIntExact(l) : l
条件无关紧要。第二个操作数("then" 部分)的类型为 int
。第三个运算符("else" 部分)的类型为 long
.
您可以阅读 Java 语言规范 15.25。条件运算符? : 以获得精确的规则。但是基本上所有的数字操作数都强制转换为"widest"类型,因为表达式结果必须是单一类型,不能return"either long
or int
",Java类型系统不强大足够了(有些语言实际上可以做到这一点)。
所以编译器基本上将该表达式重写为以下表达式:
(...) ? ((long) Math.toIntExact(l)) : l
并将long
转换为Object
,编译器插入自动装箱调用:
Long.valueOf((...) ? ((long) Math.toIntExact(l)) : l)
这就是对为什么您会看到这种行为的粗略解释。
普通的 if
语句不能 return 值,所以它只是 return 你在 return
语句中指定的任何值,没有进一步的数字提升。