在 Java 中使用 if 与三元运算符时的“错误”返回类型

Posted

技术标签:

【中文标题】在 Java 中使用 if 与三元运算符时的“错误”返回类型【英文标题】:"Wrong" return type when using if vs. ternary opertator in Java 【发布时间】:2012-08-19 23:51:38 【问题描述】:

在下面的类中,两个方法的返回类型与三元运算符的思想不一致:

return condition?a:b;

等价于

if(condition) 
    return a;
 else 
    return b;

第一个返回一个 Double,第二个返回一个 Long:

public class IfTest 
    public static Long longValue = 1l;
    public static Double doubleValue = null;

    public static void main(String[] args) 
        System.out.println(getWithIf().getClass());// outpus Long
        System.out.println(getWithQuestionMark().getClass());// outputs Double
    

    public static Object getWithQuestionMark() 
        return doubleValue == null ? longValue : doubleValue;
    

    public static Object getWithIf() 
        if (doubleValue == null) 
            return longValue;
          else 
            return doubleValue;
        
    

我可以想象这与编译器窄转换 getWithQuestionMark() 的返回类型有关,但这种语言是否明智?这肯定不是我所期望的。

欢迎任何见解!

编辑:下面有很好的答案。此外,@sakthisundar 引用的以下问题探讨了在三元运算符中发生的类型提升的另一个副作用:Tricky ternary operator in Java - autoboxing

【问题讨论】:

之前已经提出过类似的问题,原因与 Jon Skeet 的回答相同。结帐this @sakthisundar 谢谢!您指向的问题是由相同的 answer 激发的,但是从 problem 开始时我找不到它。我现在直接在问题正文中引用了它 【参考方案1】:

基本上是遵循section 15.25 of the JLS的规则,具体来说:

否则,如果第二个和第三个操作数具有可转换(第 5.1.8 节)为数字类型的类型,则有几种情况:

[...]

否则,二进制数值提升(第 5.6.2 节)将应用于操作数类型,条件表达式的类型是第二个和第三个操作数的提升类型。

所以遵循section 5.6.2,这将基本上涉及拆箱 - 所以这使您的表达式工作就像longValuedoubleValue分别是longdouble类型一样,并且扩大提升适用于long 得到double 的整体结果类型。

然后将 double 装箱,以便从方法中返回 Object

【讨论】:

啊哈!所以,1+2 给出一个整数,1+2d 给出一个双精度数,同样: 会在可能的情况下进行数字提升。仍然不是我所期望的,但比它与返回类型有任何关系要好。谢谢!【参考方案2】:

除了@Jon 的回答,看看你看到的字节码:

public static java.lang.Object getWithQuestionMark();
  Code:
   0:   getstatic       #7; //Field doubleValue:Ljava/lang/Double;
   3:   ifnonnull       16
   6:   getstatic       #8; //Field longValue:Ljava/lang/Long;
   9:   invokevirtual   #9; //Method java/lang/Long.longValue:()J
   12:  l2d
   13:  goto    22
   16:  getstatic       #7; //Field doubleValue:Ljava/lang/Double;
   19:  invokevirtual   #10; //Method java/lang/Double.doubleValue:()D
   22:  invokestatic    #11; //Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
   25:  astore_0
   26:  aload_0
   27:  areturn

如果你告诉编译器你对数字不感兴趣:

public static Object getWithQuestionMark() 
    return doubleValue == null ? (Object)longValue : (Object)doubleValue;

你会得到你想要的(字节码)

public static java.lang.Object getWithQuestionMark();
  Code:
   0:   getstatic       #7; //Field doubleValue:Ljava/lang/Double;
   3:   ifnonnull       12
   6:   getstatic       #8; //Field longValue:Ljava/lang/Long;
   9:   goto    15
   12:  getstatic       #7; //Field doubleValue:Ljava/lang/Double;
   15:  areturn

输出:

$ java IfTest
class java.lang.Long
class java.lang.Long

【讨论】:

只有一次向上转换到Object 就足够了。

以上是关于在 Java 中使用 if 与三元运算符时的“错误”返回类型的主要内容,如果未能解决你的问题,请参考以下文章

返回引用时的 C++ 三元运算符

三元运算

python lambda表达式与三元运算

为啥使用三元运算符返回字符串与在等效 if/else 块中返回的代码有很大不同?

python三元运算

为啥使用“if-else”语句会在看似相同的三元运算符构造不会产生 TypeScript 编译器错误?