V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
zhoudaiyu
V2EX  ›  Java

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

  •  
  •   zhoudaiyu · 2020-03-22 08:20:37 +08:00 via iPhone · 4578 次点击
    这是一个创建于 1486 天前的主题,其中的信息可能已经有所发展或是发生改变。

    图中的第一个 println 为啥打印的是 1.0 而不是 1 ?这个表达式应该永远执行 Integer.valueOf(1)吧?为什么被强转成 float 了?
    27 条回复    2020-03-23 17:52:11 +08:00
    Cbdy
        1
    Cbdy  
       2020-03-22 08:24:04 +08:00 via Android
    因为这两行调用的是两个不同的 println,和 Java 方法的重载机制有关
    zhoudaiyu
        2
    zhoudaiyu  
    OP
       2020-03-22 08:34:54 +08:00 via iPhone
    @Cbdy 怎么讲?
    hanshijun
        3
    hanshijun  
       2020-03-22 08:35:53 +08:00
    三目运算符会进行自动类型上升
    aneureka
        4
    aneureka  
       2020-03-22 08:37:33 +08:00 via iPhone
    因为你这个三元表达式返回的是 float,就算你返回的值是 int 也会被类型转换为 float
    xuanbg
        5
    xuanbg  
       2020-03-22 08:54:25 +08:00
    三元表达式的结果必须是同一个类型。在这里 integer 被隐式转换成 float 了。
    humpy
        6
    humpy  
       2020-03-22 09:03:19 +08:00   ❤️ 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-03-22 09:04:30 +08:00
    @humpy 好详细啊,太感谢了
    geelaw
        8
    geelaw  
       2020-03-22 09:30:33 +08:00   ❤️ 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-03-22 11:38:33 +08:00
    又学会一种回字的写法
    ae86
        10
    ae86  
       2020-03-22 12:19:16 +08:00
    路过,跟着楼主一起学习
    yeqizhang
        11
    yeqizhang  
       2020-03-22 14:18:21 +08:00 via Android
    这种问题没遇到还真不知道,网上教程也没看到提到过。之前有网上做面试题,有一道题就是这个,我做错了,这面试题真偏……
    shuqin2333
        12
    shuqin2333  
       2020-03-22 16:15:54 +08:00
    学习了
    zxCoder
        13
    zxCoder  
       2020-03-22 18:59:38 +08:00
    学习了
    lewis89
        14
    lewis89  
       2020-03-22 19:38:38 +08:00
    @zhoudaiyu #2 你用 Command + B 可以点进去 看下重载到哪个函数了
    banmuyutian
        15
    banmuyutian  
       2020-03-22 20:12:05 +08:00
    学习了
    jiom
        16
    jiom  
       2020-03-22 22:52:36 +08:00
    学习了
    757384557
        17
    757384557  
       2020-03-23 09:04:59 +08:00
    第一次看到这种问题,学习了
    niu0619
        18
    niu0619  
       2020-03-23 09:55:56 +08:00
    学习了
    yuanshuai1995
        19
    yuanshuai1995  
       2020-03-23 11:02:48 +08:00
    学写了
    yibinhp
        20
    yibinhp  
       2020-03-23 11:19:07 +08:00
    学写了
    wozhizui
        21
    wozhizui  
       2020-03-23 11:52:09 +08:00
    学习了,
    pengjl
        22
    pengjl  
       2020-03-23 14:20:03 +08:00
    学习了
    sunziren
        23
    sunziren  
       2020-03-23 16:30:02 +08:00
    学到老,活到老
    sunziren
        24
    sunziren  
       2020-03-23 16:30:16 +08:00
    学到老,活到老,学习了
    rancc
        25
    rancc  
       2020-03-23 17:04:32 +08:00
    学习了
    gotonull
        26
    gotonull  
       2020-03-23 17:30:36 +08:00
    学习了
    zhiguang
        27
    zhiguang  
       2020-03-23 17:52:11 +08:00
    又能 zb 了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1022 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 19:22 · PVG 03:22 · LAX 12:22 · JFK 15:22
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.