我在Java Puzzlers中找到了下面的谜题,
public class DosEquis {
public static void main(String[] args) {
char x = 'X';
int i = 0;
System.out.print(true ? x : 0);
System.out.print(false ? i : x);
}
}
我尝试了这段代码并运行它但输出没有根据我的猜测得出,
我的猜测输出应该是:XX
但是实际输出是:X88
我努力了解但我无法理解,是否有人能给我们解释?为什么不同的产出即将到来?因为我能够理解第一个print()
将打印变量char x
字符值,但第二个print()
打印88
是char x
中的ASCII值表示.if我简化第二个print()
中的三元运算符表达式
if(false){
System.out.print(i);
}else{
System.out.print(x);
}
然后输出即将来临XX
,很奇怪,任何人都可以解决这个问题吗?
理解三元运算符对我有很大的帮助。
提前致谢!
答案 0 :(得分:6)
这种行为的原因是三元组有一种结果类型,编译器必须提前选择,并在响应中调用print
的味道。
在true ? x : 0
案例中,0
被视为char
值,并且print(char)
被调用。在第二种情况下,由于i
是int
,x
也会隐式转换为int
(扩展转换)并调用print(int)
,从而产生数字输出。相反,隐含地将i
强制转换为char
是非法的,因为它可能会失去精确度。
静态解析类型的含义可以通过此示例显示 - 而不是print
,因为有print(Object)
,但请考虑这一点:
void method(boolean b);
void method(Integer i);
...
method(cond? false:0);
无论cond
是什么,都存在与参数兼容的重载。但是,编译器需要选择一个重载,这在编译时是不可能的。编译器将自动进行同步并将表达式指定为Object
*,但没有method(Object)
。
*实际上,我的编译器说"类型Test中的方法方法(boolean)不适用于参数( Object& Serializable& Comparable<?> )&#34但是,重点是。
答案 1 :(得分:3)
您看到的任何结果都符合JLS 15.25中指定的规则,用于确定条件操作的类型。以下是它提到的观点:
如果第二个和第三个操作数具有可转换的类型(第5.1.8节)到数字类型,那么有几种情况:
如果其中一个操作数的类型为字节或字节,另一个操作数的类型为short或Short,则条件表达式的类型很短。
如果其中一个操作数是T类型,其中T是byte,short或char,另一个操作数是int类型的常量表达式(第15.28节),其值可以在类型T中表示,那么条件表达式的类型是T.
如果其中一个操作数是T类型,其中T是Byte,Short或Character,另一个操作数是int类型的常量表达式(第15.28节),其值可以在类型U中表示。是将拆箱转换应用于T的结果,然后是类型 条件表达式是U。
否则,二进制数字提升(第5.6.2节)将应用于操作数类型,条件表达式的类型是第二个和第三个操作数的提升类型。 < / p>
在你的问题中,遵循最后一点。 x
类型为char
,而i
类型为int
类型,因此x
会提升为int
。因此,第二行的输出为88
,这是char int
的{{1}}转换。
答案 2 :(得分:2)
要了解这里发生的事情,我们需要跟踪类型。让我们依次考虑每个print语句:
System.out.print(true ? x : 0);
这里的类型是布尔值? char:const int
编译器希望ternary语句返回单个类型,它不能返回char或int。它发现0可以转换为char常量,因此将其视为'boolean? char:char'。最后一个char的值为零。因为布尔值为true,所以第一个char打印为X。
System.out.print(false ? i : x);
这里的类型是布尔值? int:char,int是一个int变量,不能被视为char,因此编译器的选择是错误,因为类型不同,缩小int或加宽char。缩小int将会失去精度,因此编译器不会在我们的后面执行它(它需要显式转换)。加宽不会松散精度,因此char被转换为int。 'X'的int值为88.因此打印值为88。
有关此转化过程的详细信息,请参阅此处:Java Language Spec
答案 3 :(得分:1)
在这一行:
System.out.print(false ? i : x);
X被转换为int,这样就打印了char X的十进制值
答案 4 :(得分:1)
使用三元运算符条件时?valTrue:valFalse。这将在两种情况下返回相同的数据类型(true或false)。在行System.out.print(condition? i : x);
中,如果为true,则为aspecting int;如果为false,则为Char,但在两种情况下,它将仅返回int。同样在行System.out.print(condition? x : i);
中,它将返回Char。
答案 5 :(得分:1)
在第一个表达式中,一侧是char,0
可分配到char
;因此第一个三元表达式是char。 char zero = 0;
在第二个中有int和char,而char是加宽:(int)'X'
是88。
常量就像在c ? "" : null
中一样,因此不会创建一个Object而是一个String(这里)。
答案 6 :(得分:1)
你的观察是正确的。但是三元运算符处理相同的数据类型。它会考虑真实值的数据类型,并在需要时转换假值的类型。在第一个print语句的情况下,三元运算符将数据类型视为Char作为x的数据类型,在第二个print语句中,三元运算符将数据类型视为int作为i的数据类型,这就是你的原因在char x中得到值的ASCII表示。
System.out.print(true ? x : 0);
/*ternary operator(compiler) consider the resulting data
type as char as the true Value type is char.*/
System.out.print(false ? i : x);
/*ternary operator(compiler) consider the resulting data
type as int as the true Value type is int.*/
/* but if you replace the print statement with if else condition
then there is no point of ternary concept at all*/
答案 7 :(得分:1)
在这样输入的三元语句中:(boolean ?
如果输入 :
,则输入 )
,只输入如果输入并且 else类型对确定最终类型很重要。编译器处理如下情况:
让我们看看这些不同的版本:
System.out.println(true ? x : 0); //X
这变为X
,因为 if type 是char
,而 else type 是可以代表0
的任何类型。常量0
在char
数据范围(0到65,535)内有效。可以将x
和0
视为int
,但int
数据范围更高(-2 31 至2 31 -1)。
根据第4点,编译器会选择较低范围的类型(char
)。
System.out.println(true ? x : i); //88
此处, if type 是char,而 else type 是int
。当您将变量声明为非最终 int
时,编译器无法确定该值是否永远不会从0
更改(即使您的代码未更改它)任何地方!)。
因此,此处仅适用第2点。 if type char
可以转换为 else type int
而不会有任何精度损失(因为int
具有更高的范围),但如果 else类型 int
转换为 if type char
,则char
的较低范围会导致精度损失(i
的值可能超出char
)。
因此,编译器选择int
以避免精度损失,如第2点。
其他一些测试用例(与上面已经解释的类似的原因):
System.out.println(false ? i : x); //88 (as per point 3)
System.out.println(false ? 0 : x); //X (as per point 4)
但是,在此代码中,结果不同:
final int i = 0;
System.out.println(true ? x : i); //X
System.out.println(false ? i : x); //X
你能说出原因吗?
* 注意:所有类型最终都可以毫无损失地转换为Object
,包括可以自动装箱的基本类型(在最近的Java版本上)。如果方法被重载以接受Object
类型,您可能永远不会遇到这种情况,因为编译器会将最终类型转换为Object
。