【问题标题】:Why does post-increment work on wrapper classes为什么后增量对包装类起作用
【发布时间】:2012-10-28 02:58:11
【问题描述】:

我正在审查一些代码,并遇到了一个实例,有人在递增一个成员变量,该成员变量是整数的包装类。我自己尝试过,真的很惊讶它的效果。

Integer x = 0; 
System.out.print(x++ + ", ");
System.out.print(x);

这会打印出0, 1,而不是我预期的0, 0。我浏览了语言规范,找不到任何内容。任何人都可以向我解释为什么这样做有效,以及跨多个平台是否安全?我原以为这会分解成

Integer x = 0;
int temp1 = x.intValue();
int temp2 = temp1 + 1;
System.out.println(temp1);
temp1 = temp2;
System.out.println(x.intValue());

但显然规范中有一些东西使它在最后一行之前添加x = temp1;

【问题讨论】:

  • 它与其说是 Integer 的包装类,不如说是 x 是 Integer 类的一个实例,以及随之而来的方法/运算符。其中之一是“++”运算符,它增加了它的值。
  • @hd1 - 这是不正确的。 ++ 运算符仅适用于(原始)整数类型。在幕后,x 被取消装箱,++ 被应用,然后将结果分配回x(在装箱转换之后)。 Integer 类没有 ++ 运算符。事实上,Integer 对象是不可变的。
  • 为什么你会期望输出是0, 0
  • @hd1:Java 不支持运算符重载,除非在一种非常特殊的情况下(字符串中的 + 运算符)。 Integer 类没有 ++ 运算符。
  • @Ted - 熟悉 C++ 的人可能还期望 Integer 的行为方式与 C++ 'const' 对象的行为方式相同。我不认为它特别直观,特别是因为预增量通常不涉及临时变量。在docs.oracle.com/javase/tutorial/java/data/numbers.htmldocs.oracle.com/javase/tutorial/java/data/autoboxing.html 的任何地方似乎都没有涵盖它

标签: java immutability post-increment


【解决方案1】:

从 Java 1.5 开始,Java 会在必要时执行自动拆箱以将“包装器类型”(例如 Integer)转换为相应的原始类型 int。然后增量运算符可以处理生成的int

【讨论】:

  • 我知道自动拆箱,这就是为什么我对 x++ 编译并不感到惊讶。我没有得到的是,java 显然也会重新装箱递增的变量并将其填充回 Integer。
【解决方案2】:

跨平台使用非常安全。行为在§15.4.2 of the Java Language Specification 中指定(强调添加):

后缀表达式的结果必须是可转换 (§5.1.8) 为数值类型的变量,否则会发生编译时错误。

后缀自增表达式的类型就是变量的类型。后缀自增表达式的结果不是变量,而是值。

在运行时,如果操作数表达式的求值突然完成,则后缀增量表达式出于同样的原因突然完成并且不会发生增量。否则,将值 1 添加到变量的值并将总和存储回变量。在加法之前,对值 1 和变量的值执行二进制数值提升 (§5.6.2)。如有必要,在存储变量之前,可通过缩小原语转换 (§5.1.3) 和/或装箱转换 (§5.1.7) 将总和缩小为变量的类型。后缀自增表达式的值是新值存储前变量的值。

编辑下面是您的示例代码中发生的更准确的等价物:

Integer x = 0;
int temp = x.intValue();
x = temp + 1; // autoboxing!
System.out.println(temp + ", ");
System.out.println(x.intValue());

【讨论】:

  • +1 "如有必要,总和是...进行装箱转换"
  • 只是想补充一点,整数是不可变的,所以在这结束之后 x 实际上是一个不同的对象。
【解决方案3】:

这是 Java 5 中称为 autoboxing 的“新”功能 - 整数在需要时转换为 in - 反之亦然。同样适用于 Float、Double、Boolean 等。

虽然这是一个方便的功能,但如果你不小心,它可能会导致巨大的性能损失

【讨论】:

    【解决方案4】:

    请注意,如果将增量操作移至方法,则输出将如预期:

    public void printIncrement(Integer x1) {
        System.out.print(x1++ + ", ");
    }
    

    这是因为 Java 传递了引用的副本,所以您现在正在使用一个新的引用对象,x1 而不是 x。 x 指向的原始 Integer 对象保持不变,x 仍然指向它。

    如果您忘记了在方法中进行计算时,这很容易咬到您。

    【讨论】:

      【解决方案5】:

      Tedd Hopp 爵士的回答对于只编程几年或几年的程序员来说是一个非常复杂的水平。

      让我以简单的方式消除您的疑问 假设

          Integer x=10;
          x++;
          System.out.println(x) ;
      

      输出将是 11

      因为 ++ 无论是后自增还是预自增都只是在内部进行加法 1

      即 x+1 是它必须执行的并将结果放回同一个变量中。

      ie x=x+1;
      

      现在我们都知道 + 运算符只能接受原语,而 x 是对象, 然后我们有自动拆箱和自动装箱的概念。 所以表达式变成了

      x.intValue()+1;//step 1 auto-unboxing
      
      x=Integer.valueOf(x.intValue()+1);//step 2 auto-boxing
      

      因此,在 println 语句中的另一个自动拆箱步骤之后,输出为 11。

      【讨论】:

        猜你喜欢
        • 2022-12-17
        • 1970-01-01
        • 1970-01-01
        • 2014-05-19
        • 2019-08-10
        • 1970-01-01
        • 2016-03-25
        • 1970-01-01
        • 2019-11-15
        相关资源
        最近更新 更多