有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

eclipse在Java7中允许从数字转换为双精度?(自动装箱)

一位同事签入此代码:

    Number n = ...;
    double nr = n == null ? 0.0 : (double) n;

另一位同事随后抱怨说,这没有编译,这正是我所期望的。然而,事实证明,我已经从SVN中提取了这段代码,并且一切正常。在eclipse中,我们都将Java版本设置为1.7,结果证明,在Eclipse4.4.2(Luna)下,代码可以很好地编译,但在4.2.2下失败

我用n.doubleValue()替换了cast,解决了这个问题

现在真正的问题是:为什么这一点首先被接受?当然,它应该在向Double而不是double施放时起作用,但我认为直接从Number施放到double是不允许的。那么,这是Eclipse4.2.2中同时修复的一个bug,还是Eclipse4.4.2默默地接受不应该编译的代码(哪一个是bug)


共 (3) 个答案

  1. # 1 楼答案

    抽象类编号是BigDecimal、BigInteger、Byte、Double、Float、Integer、Long和Short类的超类

    Double是Double的包装类,支持自动装箱,但不支持数字。换句话说,Number可以包含许多不是双精度的包装器类,所以需要显式转换。 请尝试以下代码:

    Number n = 78.3145;
    double nr = (double) n;
    Double d = 3.1788;
    double dr = d;
    System.out.println(n);
    System.out.println(dr);
    
  2. # 2 楼答案

    在Java7中,为了允许使用^{}s,强制转换系统必须在原语类型方面稍微改变。调用方法句柄时,javac编译器会生成一个so-called polymorhic signature,它来自方法句柄的方法签名。这些多态性签名是通过使用强制转换提示参数的类型来创建的。例如,当绑定签名为double, long -> int的方法时,需要进行以下转换:

    Number foo = 42d, bar = 43L;
    int ignored = (int) methodHandle.invoke((double) object, (long) bar);
    

    但是MethodHandle::invoke的源代码签名被定义为Object[] -> Object,如果不直接将值转换为基元类型,则无法生成多态签名

    显然,为了实现这一点,必须对Java编译器进行更改,以允许以前不合法的铸造。虽然从理论上讲,可以将这种铸件的使用限制在用@PolymorhicSignature注释的方法上,但这会导致一个奇怪的例外,为什么现在在javac中,当不创建多态性签名时生成适当的字节码时,它现在通常是可能的。然而,原语类型仍然代表它们自己的运行时类型,这一点在另一个答案中指出,该答案将这种转换生成的字节码发布在MethodHandle之外

    Object foo = 42;
    int.class.cast(foo);
    

    将导致运行时异常

    然而,我同意以下评论,即这不一定在JLS中得到适当讨论,但我同意。有人提到,一旦lambda表达式就位,规范就应该相应地更新,但Java 8的JLS似乎没有提到此类铸件或@PolymorphicSignature。同时,声明禁止任何未明确允许的转换

    可以说,JLS目前落后于javac实现,Eclipse编译器肯定没有正确地处理这个问题。您可以将其与一些corner-cases of generic type inference进行比较,在这些corner-cases of generic type inference中,直到今天,几个IDE编译器的行为都不同于javac

  3. # 3 楼答案

    如注释中所述,数字没有相应的基元类型,但是Java编译器似乎很聪明,一旦你进行了转换,就可以在后台执行转换

    下面是一个示例测试用例

    包装试验

    公共类编号rautoboxing{

    public static void main(String[] args) {
        Number n =new Long(1);
        double nr = (double) n;
        Integer i= 1;//boxing
        Integer j= new Integer(1);
        int k=j;//unboxing
        System.out.print("n="+n+" nr=" + nr + " i="+ i + " k=" + k);
    }
    

    }

    我反编译了文件。类(我在windows上测试了jdk 7和8,结果是一样的),这就是结果

    import java.io.PrintStream;
    
    public class NumberAutoboxing
    {
      public static void main(String[] args)
      {
        Number n = new Long(1L);
        double nr = ((Double)n).doubleValue();
        Integer i = Integer.valueOf(1);
        Integer j = new Integer(1);
        int k = j.intValue();
        System.out.print("n=" + n + " nr=" + nr + " i=" + i + " k=" + k);
      }
    }
    

    正如您所看到的,从数字到双精度的转换是以与取消装箱示例相同的方式进行的(从整数到整数)这是因为编译器将强制转换double更改为Double,从而允许取消装箱。似乎有两种交际方式(或者我在这一种上推测的类似方式)

    1. 意识到既然n是一个数字,演员必须从 双人对双人
    2. 拆箱