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

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

  •  
  •   zhoudaiyu · 10 天前 via iPhone · 2646 次点击

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