假设我们有一个StringBuilder和一个double。现在想要追加双倍。如果double可以表示为Integer(例如3.0,5.0等),我想将其添加为Integer,否则为double。
实现这一目标的第一种方法是:
StringBuilder sb = new StringBuilder();
double d = 3.5;
if (d % 1 == 0) sb.append((int) d);
else sb.append(d);
System.out.println(sb.toString());
如果d
为3.0
3将被追加,如果d
为3.5
将会附加3.5,则效果仍然有效。
现在我想用三元运算符很快做到这一点:
StringBuilder sb = new StringBuilder();
double d = 3.5;
sb.append(d % 1 == 0 ? (int) d : d);
System.out.println(sb.toString());
所以现在我有一个问题,每次,如果double为3.0
或3.5
,它将被添加为double值!只有当我理论上施放在true
和false
时,它才有效......但每次都不是我想要的。这是什么问题?为什么三元运算符不起作用?
答案 0 :(得分:4)
JLS-15.25中记录了此行为。 Conditional Operator ? : :
如果其中一个操作数是T类型,其中T是byte,short或char,另一个操作数是int类型的常量表达式(第15.28节),其值可以在类型T中表示,那么类型为条件表达式是T
写作时
(a > b) ? 'a' : 65
第二种类型转换为char。
通过JLS,它解释了其他情况下的行为(相同的方法)。
答案 1 :(得分:2)
这种行为的原因是具有三元运算符的表达式具有明确定义的类型。 JLS详细描述了如何评估此类型,但粗略地说,它是冒号前表达式类型的最小上限和冒号后表达式的类型。
例如,如果b
为boolean
,i
为int
且d
为double
,则{{1}的类型是b ? i : d
,因为double
是double
和int
的最小上限。当您在double
上致电append( b ? i : d )
时,您会获得StringBuilder
参数的append
版本。在您的情况下,同样的事情发生在double
。
答案 2 :(得分:1)
在原始数字上使用三元运算符时,第二个和第三个操作数受二进制数字提升。在您的情况下,int
会转换为double
。这在JLS #15.25。
答案 3 :(得分:1)
方法StringBuilder.append()
对于不同类型的参数有很多重载。使用哪种方法重载是编译时决策。三元运算符的结果只有一种类型,在这种情况下为int
或double
- double
。
对于if
语句,编译器根据分支使用正确的append()
方法重载。
答案 4 :(得分:0)
我认为编译器将sb.append(d % 1 == 0 ? (int) d : d)
视为sb.append(double)
,否则将sb.append((int) d)
视为sb.append(int)
。
答案 5 :(得分:0)
数字条件表达式是独立表达式(第15.2节)。
数字条件表达式的类型确定如下:
如果第二个和第三个操作数具有相同的类型,那么这就是条件表达式的类型。
如果第二个和第三个操作数之一是原始类型T,而另一个操作数的类型是将装箱转换(第5.1.7节)应用于T的结果,则条件表达式的类型为T. / p>
如果其中一个操作数是byte或Byte类型而另一个是short或Short类型,则条件表达式的类型很短。
如果其中一个操作数是T类型,其中T是byte,short或char,另一个操作数是int类型的常量表达式(第15.28节),其值可以在类型T中表示,那么类型为条件表达式是T。
如果其中一个操作数是T类型,其中T是Byte,Short或Character,另一个操作数是int类型的常量表达式,其值可以在类型U中表示,这是应用拆箱转换的结果到T,那么条件表达式的类型是U。
否则,二进制数字提升(第5.6.2节)将应用于操作数类型,条件表达式的类型是第二个和第三个操作数的提升类型。
将类型转换为相应的原始类型,称为拆箱。 如果一个操作数是常量int(在取消装箱之前不是整数),其值可以在另一个类型中表示,则int操作数将转换为另一个类型。 否则,较小的类型将转换为下一个更大的类型,直到两个操作数具有相同的类型。转换订单为:
字节 - >短 - > int - >长 - >浮动 - >双
char - > int - >长 - >浮动 - >双
最终,整个条件表达式获取其第二个和第三个操作数的类型。