zhoudaiyu
V2EX  ›  Java

Java 纯新手,问个关于类型的问题

By zhoudaiyu at 2020 年 3 月 22 日 · 6118 次点击

图中的第一个 println 为啥打印的是 1.0 而不是 1 ?这个表达式应该永远执行 Integer.valueOf(1)吧?为什么被强转成 float 了?
27 条回复  •  2020-03-23 17:52:11 +08:00
Cbdy
   1
Cbdy  
   2020 年 3 月 22 日 via Android
因为这两行调用的是两个不同的 println,和 Java 方法的重载机制有关
zhoudaiyu
   2
zhoudaiyu  
OP
   2020 年 3 月 22 日 via iPhone
@Cbdy 怎么讲?
hanshijun
   3
hanshijun  
   2020 年 3 月 22 日
三目运算符会进行自动类型上升
aneureka
   4
aneureka  
   2020 年 3 月 22 日 via iPhone
因为你这个三元表达式返回的是 float,就算你返回的值是 int 也会被类型转换为 float
xuanbg
   5
xuanbg  
   2020 年 3 月 22 日
三元表达式的结果必须是同一个类型。在这里 integer 被隐式转换成 float 了。
humpy
   6
humpy  
   2020 年 3 月 22 日   ❤️ 27
两个操作数分别是 Integer 类型和 Float 类型,根据 [jls-15.25.2]( https://docs.oracle.com/javase/specs/jls/se14/html/jls-15.html#jls-15.25.2 ),条件表达式的结果类型是两个操作数类型提升后的类型:

「 Otherwise, general numeric promotion (§5.6) is applied to the second and third operands, and the type of the conditional expression is the promoted type of the second and third operands.」


根据 [jls-5.6]( https://docs.oracle.com/javase/specs/jls/se14/html/jls-5.html#jls-5.6 ) 描述的类型提升规则:

1. 两个操作数拆包;
2. 拆包后,一个操作数是 int,一个是 float,将 int 扩展为 float

「
1. If any expression is of a reference type, it is subjected to unboxing conversion ( §5.1.8 ).
2. Next, widening primitive conversion ( §5.1.2 ) and narrowing primitive conversion ( §5.1.3 ) are applied to some expressions, according to the following rules:
• If any expression is of type double, then the promoted type is double, and other expressions that are not of type double undergo widening primitive conversion to double.
• Otherwise, if any expression is of type float, then the promoted type is float, and other expressions that are not of type float undergo widening primitive conversion to float.
」

因此这个语句最终的结果是 float 。


---
可以写一段简单的代码看一下这个过程:

❯ cat a.java
class a {

public static void main(String[] args) {
System.out.println(true ? Integer.valueOf(1) : Float.valueOf(3));
}
}

查看它编译后的字节码,可以看到「 10: i2f 」这行确实做了 int -> float 的类型提升:

❯ javap -v a.class
...
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: iconst_1
4: invokestatic #3 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
7: invokevirtual #4 // Method java/lang/Integer.intValue:()I
10: i2f
11: invokevirtual #5 // Method java/io/PrintStream.println:(F)V
14: return
zhoudaiyu
   7
zhoudaiyu  
OP
   2020 年 3 月 22 日
@humpy 好详细啊,太感谢了
geelaw
   8
geelaw  
   2020 年 3 月 22 日   ❤️ 5
@humpy #6 感谢查阅文档,值得注意的是这里的类型转换会尝试 unbox,导致 cond ? a : b 里面若 a 、b 具有原始类型的引用版本且其中一个是 null,则可能导致意外的空引用异常,这个可以加入 Java 反人类设计之一了。

public class HelloWorld
{
public static void main(String[] args)
{
System.out.println((Integer)null);
System.out.println((Object)(Integer)null);
System.out.println((Float)null);
System.out.println((Object)(Float)null);
System.out.println(true ? (Object)(Integer)null : Float.valueOf(1.0f));
System.out.println(true ? (Integer)null : (Object)Float.valueOf(1.0f));
// 下面这行会抛出异常
System.out.println(true ? (Integer)null : Float.valueOf(1.0f));
}
}

相应的,在 C# 里则无此问题——大多数情况下无法产生声明类型是值类型但被当作引用类型的表达式。最接近的两个情况:

可空类型的转换是正常的(这点比 Java 自然很多),表达式 true ? (int?)null : (float?)1.0f 等同于 (float?)null 。

值为装箱后的值类型的引用类型转换是正常的(这点同 Java ),表达式 true ? (IConvertible)(int?)null : (float?)1.0f 等同于 (object)null 。
hhhsuan
   9
hhhsuan  
   2020 年 3 月 22 日
又学会一种回字的写法
ae86
   10
ae86  
   2020 年 3 月 22 日
路过,跟着楼主一起学习
yungo8
   11
yungo8  
   2020 年 3 月 22 日 via Android
这种问题没遇到还真不知道,网上教程也没看到提到过。之前有网上做面试题,有一道题就是这个,我做错了,这面试题真偏……
shuqin2333
   12
shuqin2333  
   2020 年 3 月 22 日
学习了
zxCoder
   13
zxCoder  
   2020 年 3 月 22 日
学习了
lewis89
   14
lewis89  
   2020 年 3 月 22 日
@zhoudaiyu #2 你用 Command + B 可以点进去 看下重载到哪个函数了
banmuyutian
   15
banmuyutian  
   2020 年 3 月 22 日
学习了
jiom
   16
jiom  
   2020 年 3 月 22 日
学习了
757384557
   17
757384557  
   2020 年 3 月 23 日
第一次看到这种问题,学习了
niu0619
   18
niu0619  
   2020 年 3 月 23 日
学习了
yuanshuai1995
   19
yuanshuai1995  
   2020 年 3 月 23 日
学写了
yibinhp
   20
yibinhp  
   2020 年 3 月 23 日
学写了
wozhizui
   21
wozhizui  
   2020 年 3 月 23 日
学习了,
pengjl
   22
pengjl  
   2020 年 3 月 23 日
学习了
sunziren
   23
sunziren  
   2020 年 3 月 23 日
学到老,活到老
sunziren
   24
sunziren  
   2020 年 3 月 23 日
学到老,活到老,学习了
rancc
   25
rancc  
   2020 年 3 月 23 日
学习了
gotonull
   26
gotonull  
   2020 年 3 月 23 日
学习了
zhiguang
   27
zhiguang  
   2020 年 3 月 23 日
又能 zb 了
© 2026 V2EX · 93ms · 3.9.8.5