V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
zhoudaiyu
V2EX  ›  Java

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

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