Java自动装箱为什么不扩展到自动装箱类型的方法的方法调用?

Java自动装箱为什么不扩展到自动装箱类型的方法的方法调用?

Why doesn't Java autoboxing extend to method invocations of methods of the autoboxed types?

我想将原语转换为字符串,然后尝试:

1
myInt.toString();

此操作失败,并显示以下错误:

1
int cannot be dereferenced

现在,我知道原语不是引用类型(即,不是对象),因此不能有方法。但是,Java 5引入了自动装箱和拆箱功能(一种C#...我在C#中从未喜欢过,但这不重要)。因此,通过自动装箱,我希望上面的将myInt转换为Integer,然后在其上调用toString()。

此外,我相信C#允许这样的调用,除非我记错了。这仅仅是Java的自动装箱/拆箱规范的不幸缺点,还是有充分的理由呢?


Java自动装箱/拆箱的程度无法允许您取消引用原语,因此编译器会禁止它。您的编译器仍然知道myInt作为原语。在jcp.org上有关于此问题的论文。

自动装箱主要在分配或参数传递期间有用-允许您将图元作为对象传递(反之亦然),或将图元分配给对象(反之亦然)。

不幸的是,您必须这样做:(对Patrick表示敬意,我切换到了您的方式)

1
Integer.toString(myInt);


贾斯汀所说的同上,但你应该这样做:

1
Integer.toString(myInt);

它保存一两个分配,并且可读性更好。


另一种方法是使用:

1
String.valueOf(myInt);

对于每种原始类型和Object,此方法都重载。这样,您甚至不必考虑所使用的类型。方法的实现将为您调用给定类型的适当方法,例如Integer.toString(myInt)

请参阅http://java.sun.com/javase/6/docs/api/java/lang/String.html。


seems like a shortcoming of the
specification to me

缺点更多,这是一个微妙的话题。检查一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class methodOverloading{
   public static void hello(Integer x){
      System.out.println("Integer");
   }

   public static void hello(long x){
      System.out.println("long");
   }

   public static void main(String[] args){
      int i = 5;
      hello(i);
   }
}

这里会打印" long"(我自己没有检查过),因为编译器选择加宽而不是自动装箱。使用自动装箱时请务必小心,否则请不要使用它!


最接近您的示例的有效语法是

1
((Integer) myInt).toString();

编译器完成后,等同于

1
Integer.valueOf(myInt).toString();

但是,它的表现不如常规用法String.valueOf(myInt),因为除特殊情况外,它会创建一个新的Integer实例,然后立即将其丢弃,从而导致更多不必要的垃圾。 (缓存了一小部分整数,并通过数组访问。)也许出于性能原因,语言设计人员希望阻止这种用法。

编辑:如果下降投票者会对此无用的原因发表评论,我将不胜感激。


如果Java定义了某些对原始类型进行操作的静态方法,并在编译器中内置了一些语法糖,那么这将很有帮助,这样

1
5.asInteger

将等同于

1
some.magic.stuff.Integer.asInteger(5);

我认为这样的功能不会导致与根据当前规则编译的任何代码不兼容,并且在许多情况下都不会减少语法混乱。如果Java要对被取消引用的原语进行自动装箱,人们可能会认为它会将取消引用的语法映射到静态方法调用(这实际上是.NET中发生的事情),因此以这种形式编写的操作不会比原来花费更多的钱。等效的静态方法调用。添加新的语言功能会鼓励人们编写错误的代码(例如,自动装箱后解引用的原语)似乎不是一个好主意,尽管允许使用解引用样式的方法可能是。


正如每个人都指出的那样,自动装箱可以使您简化一些代码,但是您不能假装原语是复杂的类型。

也很有趣:Java中的"自动装箱是编译器级的技巧"。自动装箱基本上是添加到Java上的一个奇怪的麻烦。查看这篇文章,了解有关它有多奇怪的更多信息。


在C#中,整数既不是引用类型,也不必将其装箱才能调用ToString()。但是,它们在框架中被视为对象(作为ValueType,因此它们具有值语义)。在CLR中,通过"间接"将它们加载到堆栈(ldind)上来调用基元上的方法。


推荐阅读