zhoudaiyu
V2EX  ›  Java

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

  •  
  •   zhoudaiyu · Mar 22, 2020 via iPhone · 6292 views
    This topic created in 2422 days ago, the information mentioned may be changed or developed.

    图中的第一个 println 为啥打印的是 1.0 而不是 1 ?这个表达式应该永远执行 Integer.valueOf(1)吧?为什么被强转成 float 了?
    27 replies  •  2020-03-23 17:52:11 +08:00
    Cbdy
    Cbdy
    Mar 22, 2020 via Android
    1
    因为这两行调用的是两个不同的 println,和 Java 方法的重载机制有关
    zhoudaiyu
    zhoudaiyu
    OP
    Mar 22, 2020 via iPhone
    2
    @Cbdy 怎么讲?
    hanshijun
    hanshijun
    Mar 22, 2020
    3
    三目运算符会进行自动类型上升
    aneureka
    aneureka
    Mar 22, 2020 via iPhone
    4
    因为你这个三元表达式返回的是 float,就算你返回的值是 int 也会被类型转换为 float
    xuanbg
    xuanbg
    Mar 22, 2020
    5
    三元表达式的结果必须是同一个类型。在这里 integer 被隐式转换成 float 了。
    humpy
    humpy
    Mar 22, 2020❤️27
    6
    两个操作数分别是 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
    zhoudaiyu
    OP
    Mar 22, 2020
    7
    @humpy 好详细啊,太感谢了
    geelaw
    geelaw
    Mar 22, 2020❤️5
    8
    @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
    hhhsuan
    Mar 22, 2020
    9
    又学会一种回字的写法
    ae86
    ae86
    Mar 22, 2020
    10
    路过,跟着楼主一起学习
    yungo8
    yungo8
    Mar 22, 2020 via Android
    11
    这种问题没遇到还真不知道,网上教程也没看到提到过。之前有网上做面试题,有一道题就是这个,我做错了,这面试题真偏……
    shuqin2333
    shuqin2333
    Mar 22, 2020
    12
    学习了
    zxCoder
    zxCoder
    Mar 22, 2020
    13
    学习了
    lewis89
    lewis89
    Mar 22, 2020
    14
    @zhoudaiyu #2 你用 Command + B 可以点进去 看下重载到哪个函数了
    banmuyutian
    banmuyutian
    Mar 22, 2020
    15
    学习了
    jiom
    jiom
    Mar 22, 2020
    16
    学习了
    757384557
    757384557
    Mar 23, 2020
    17
    第一次看到这种问题,学习了
    niu0619
    niu0619
    Mar 23, 2020
    18
    学习了
    yuanshuai1995
    yuanshuai1995
    Mar 23, 2020
    19
    学写了
    yibinhp
    yibinhp
    Mar 23, 2020
    20
    学写了
    wozhizui
    wozhizui
    Mar 23, 2020
    21
    学习了,
    pengjl
    pengjl
    Mar 23, 2020
    22
    学习了
    sunziren
    sunziren
    Mar 23, 2020
    23
    学到老,活到老
    sunziren
    sunziren
    Mar 23, 2020
    24
    学到老,活到老,学习了
    rancc
    rancc
    Mar 23, 2020
    25
    学习了
    gotonull
    gotonull
    Mar 23, 2020
    26
    学习了
    zhiguang
    zhiguang
    Mar 23, 2020
    27
    又能 zb 了
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   1466 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 60ms · UTC 04:43 · PVG 12:43 · LAX 20:43 · JFK 23:43
    ♥ Do have faith in what you're doing.