繁体   English   中英

autoboxing是否调用valueOf()?

[英]Does autoboxing call valueOf()?

我正在尝试确定以下语句是否保证是真的:

((Boolean)true) == Boolean.TRUE
((Boolean)true) == Boolean.valueOf(true)
((Integer)1) == Integer.valueOf(1)

我一直认为autoboxing相当于在相应的类型上调用valueOf() 我在这个主题上看到的每一个讨论似乎都支持我的假设。 但我在JLS中找到的只有以下内容( §5.1.7 ):

如果值p被装箱是文本类型的整数int之间-128127包容(§3.10.1)或布尔值truefalse (§3.10.3),或一个之间文字字符'\''\'包含(§3.10.4),然后让ab成为p的任意两次拳击转换的结果。 a == b总是如此。

这描述了 valueOf()相似的行为*。 但似乎没有任何保证valueOf()实际上被调用,这意味着理论上可以有一个实现为自动装箱值保留一个单独的专用缓存。 在这种情况下,缓存的自动装箱值与常规缓存的装箱值之间可能不存在标识相同性。

Oracle的自动装箱教程事实上说明了li.add(i)被编译为li.add(Integer.valueOf(i)) ,其中i是一个int 但我不知道该教程是否应被视为权威来源。


*它比valueOf()稍微弱一些,因为它只引用文字值。

我首先提出你的问题是编译器为自动装箱生成了什么代码?

但是,在您对@ElliottFrisch发表评论后,我发现它有所不同:

我知道编译器的行为方式。 我想弄清楚这种行为是否得到保证。

对于其他读者,假设“行为方式”意味着使用valueOf

请记住,Java有多个编译器。 为了“合法”,他们必须遵守JLS中给出的合同。 因此,只要遵守此处的所有规则,就无法保证内部如何实施自动装箱。

但我没有看到任何理由不使用valueOf ,特别是它使用缓存值,并且是Joseph D. Darcy在本文中推荐的方法。

在语言规范提到它之前,不能保证自动装箱等同于对静态valueOf方法的调用。 它是一个实现方面,不是装箱转换规范的一部分。 理论上,只要符合您在JLS中提到的规则,实现就可以自由使用其他机制。

在实践中,有许多Sun JDK错误报告(例如JDK-4990346JDK-6628737 )明确暗示当在Java 5中引入自动装箱时,目的是让编译器依赖于JDK-6628737中所述的valueOf

JDK 5中为javac引入了静态工厂方法Integer.valueOf(int),Long.valueOf(long)等,以实现自动装箱规范所需的缓存行为。

但这只适用于javac,不一定是所有编译器。

自动装箱绝对执行valueOf() ...在OpenJDK的。 如果这是您的实施,请继续阅读......如果没有,请跳至下方。

((Boolean)true) == Boolean.TRUE
((Boolean)true) == Boolean.valueOf(true)

Java文档声明Boolean.valueOf()始终返回Boolean.TRUEBoolean.FALSE ,因此在这些情况下您的引用比较将成功。

((Integer)1) == Integer.valueOf(1)

对于这个特定的例子,在具有默认设置的OpenJDK实现下,它可能会因为您选择了一个在启动时缓存的值<128这一事实而工作(虽然这可以作为命令行arg重写)。 它也用于较大的值工作,如果是经常使用的,足以被缓存。 除非您在关于Integer缓存的“安全”假设下工作,否则不要期望引用比较是相等的。

LongShortCharacterByte偶然实现了这个缓存,但与Integer不同,它不可调。 如果你比较autobox / valueOf()引用, Byte将始终有效,因为很明显,你不能超出范围。 FloatDouble毫不奇怪总是会创建一个新实例。


现在,用纯粹的通用术语? 请参阅JLS的这一部分 - 您必须在-128到127范围内为boolean和任何intchar提供相同的引用。 其他任何事情都无法保证

Oracle的自动装箱教程事实上说明了li.add(i)被编译为li.add(Integer.valueOf(i)),其中i是一个int。 但我不知道该教程是否应被视为权威来源。

我正在运行Oracle Java 1.7.0_72,看起来它确实使用了valueOf。 下面是一些代码和它的字节码。 字节码显示它正在使用valueOf。

public class AutoBoxing {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        Integer x = 5;
        int i = x;
        System.out.println(x.toString());
    }

}





Compiled from "AutoBoxing.java"
public class testing.AutoBoxing {
  public testing.AutoBoxing();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_5
       1: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
       4: astore_1
       5: aload_1
       6: invokevirtual #3                  // Method java/lang/Integer.intValue:()I
       9: istore_2
      10: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
      13: aload_1
      14: invokevirtual #5                  // Method java/lang/Integer.toString:()Ljava/lang/String;
      17: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      20: return

但我不知道Open JDK使用的是什么。 会尝试一下。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM