Java 为什么有些语言需要装箱和拆箱?

Java 为什么有些语言需要装箱和拆箱?,java,c++,generics,boxing,unboxing,Java,C++,Generics,Boxing,Unboxing,这不是什么是装箱和拆箱的问题, 这相当于为什么像Java和C#这样的语言需要这个呢 我非常熟悉C++、STL和Booost。 在C++中,我可以很容易地写出这样的东西, std::vector<double> dummy; std::向量虚拟; 我有一些Java方面的经验,但我真的很惊讶,因为我不得不写这样的东西 ArrayList<Double> dummy = new ArrayList<Double>(); ArrayList dummy=new

这不是什么是装箱和拆箱的问题, 这相当于为什么像Java和C#这样的语言需要这个呢

我非常熟悉C++、STL和Booost。 在C++中,我可以很容易地写出这样的东西,

std::vector<double> dummy;
std::向量虚拟;
我有一些Java方面的经验,但我真的很惊讶,因为我不得不写这样的东西

ArrayList<Double> dummy = new ArrayList<Double>();
ArrayList dummy=new ArrayList();

我的问题是,为什么它应该是一个对象,在谈到泛型时,从技术上讲,包含基元类型有什么困难?

装箱和拆箱是语言(如C#和Java)实现其内存分配策略的必要条件

某些类型在堆栈上分配,其他类型在堆上分配。为了将堆栈分配的类型视为堆分配的类型,需要装箱以将堆栈分配的类型移动到堆上。拆箱是相反的过程

在C#中,堆栈分配的类型称为值类型(例如
System.Int32
System.DateTime
),堆分配的类型称为引用类型(例如
System.Stream
System.String


在某些情况下,将值类型视为引用类型是有利的(反射就是一个例子),但在大多数情况下,最好避免装箱和取消装箱。

我认为这也是因为原语不会从对象继承。假设您有一个方法希望能够接受任何东西作为参数,例如

class Printer {
    public void print(Object o) {
        ...
    }
}
您可能需要向该方法传递一个简单的基元值,如:

printer.print(5);
您可以在不装箱/取消装箱的情况下执行此操作,因为5是基本体,而不是对象。您可以为每个基元类型重载print方法以启用此类功能,但这是一个难题。

在Java和C#(与C++不同)中,一切都扩展了对象,因此像ArrayList这样的集合类可以保存对象或其任何子体(基本上是任何东西)

然而,出于性能原因,java中的原语或C#中的值类型被赋予了特殊的状态。它们不是目标。您不能执行以下操作(在Java中):

即使toString是对象上的一个方法。为了将这一点与性能联系起来,创建了等效的对象。自动装箱删除了必须将原语放入其包装类并再次取出的样板代码,从而使代码更具可读性

C#中的值类型和对象之间的差异更为灰色。看看他们有什么不同

在谈到泛型时,从技术上讲,包含基元类型有什么困难

就Java而言,这是因为泛型的工作方式。在Java中,泛型是一种编译时技巧,它阻止您将
图像
对象放入
数组列表
。但是,Java的泛型是通过类型擦除实现的:泛型类型信息在运行时丢失。这是出于兼容性的原因,因为泛型是在Java生命的后期添加的。这意味着,在运行时,
ArrayList
实际上是一个
ArrayList
(或者更好:只是
ArrayList
,它在所有方法中期望并返回
Object
),在检索值时自动转换为
String

但是由于
int
不是从
Object
派生的,因此不能将其放入(运行时)期望
Object
的数组列表中,也不能将
Object
强制转换为
int
。这意味着必须将原语
int
包装为从
对象
继承的类型,如
整数


例如,C#的工作原理不同。C#中的泛型也在运行时强制执行,并且不需要对
列表进行装箱。C#中的装箱仅在尝试将值类型(如
int
)存储在引用类型变量(如
object
)中时发生。由于C#中的
int
继承自C#中的
Object
,因此写入
Object obj=2
是完全有效的,但是int将被装箱,这是由编译器自动完成的(没有
Integer
引用类型向用户公开或任何内容).

我只能告诉您为什么Java在泛型中不支持primitve类型

首先是一个问题,每次支持这个问题都会引起关于java是否应该有基元类型的讨论。这当然阻碍了对实际问题的讨论

第二个不包括它的主要原因是他们想要二进制向后兼容性,这样它就可以在不知道泛型的VM上不加修改地运行。这种向后兼容性/迁移兼容性也是为什么现在Collections API支持泛型并保持不变,而没有一套全新的泛型感知集合API(正如C#引入泛型时一样)

兼容性是使用ersure(在编译时删除的泛型类型参数信息)实现的,这也是在java中获得如此多未检查强制转换警告的原因

您仍然可以添加具体化的泛型,但这并不容易。仅仅添加类型信息添加运行时而不是删除它将不起作用,因为它破坏了源代码和二进制代码的兼容性(您不能继续使用原始类型,也不能调用现有的编译代码,因为它们没有相应的方法)

另一种方法是C#选择的方法:见上文

这个用例不支持自动装箱/取消装箱,因为自动装箱成本太高


堆上存储的每个非数组非字符串对象都包含一个8或16字节的头(大小适用于32/64位系统),后跟该对象的公共和私有字段的内容。数组和字符串有上面的头,再加上一些字节,定义数组的长度和每个元素的大小(可能是
 7.toString()