我主要使用Java,泛型相对较新.我一直读到Java做出了错误的决定.NET有更好的实现等等.

那么,C++、C#和Java在泛型上的主要区别是什么呢?各有优劣吗?

推荐答案

我会加入我的声音,try 让事情变得清晰:

C#泛型允许您声明类似的内容.

List<Person> foo = new List<Person>();

and then the compiler will prevent you from putting things that aren't Person into the list.
Behind the scenes the C# compiler is just putting List<Person> into the .NET dll file, but at runtime the JIT compiler goes and builds a new set of code, as if you had written a special list class just for containing people - something like ListOfPerson.

这样做的好处是它真的很快.没有强制转换或任何其他内容,并且因为DLL包含的信息是这是一个Person的列表,所以其他稍后使用反射查看它的代码可以告诉您它包含Person个对象(因此您获得了智能感知等等).

这样做的缺点是,旧的C#1.0和1.1代码(在添加泛型之前)不理解这些新的List<something>,因此您必须手动将其转换回普通的旧List,以便与它们进行互操作.这不是什么大问题,因为C#2.0二进制代码不向后兼容.只有当你把一些旧的C#1.0/1.1代码升级到C#2.0时才会发生这种情况

Java泛型允许您声明如下内容.

ArrayList<Person> foo = new ArrayList<Person>();

表面上看起来是一样的,而且有点像.编译器还将阻止您将不是Person的内容放入列表中.

The difference is what happens behind the scenes. Unlike C#, Java does not go and build a special ListOfPerson - it just uses the plain old ArrayList which has always been in Java. When you get things out of the array, the usual Person p = (Person)foo.get(1); casting-dance still has to be done. The compiler is saving you the key-presses, but the speed hit/casting is still incurred just like it always was.
When people mention "Type Erasure" this is what they're talking about. The compiler inserts the casts for you, and then 'erases' the fact that it's meant to be a list of Person not just Object

这种方法的好处是,不理解泛型的旧代码不必在意.它仍在处理与以往一样的旧ArrayList.这在java世界中更为重要,因为他们希望支持使用java 5和泛型编译代码,并让它在旧的1.4或以前的JVM上运行,而微软故意决定不go 理会.

缺点是我前面提到的速度冲击,而且因为没有ListOfPerson个伪类或类似的东西进入系统.类文件,以及稍后查看它的代码(带有反射,或者如果您从另一个集合中提取它,在该集合中它被转换为Object等等),无法以任何方式判断它是一个只包含Person的列表,而不是任何其他数组列表.

C++模板允许你声明类似的东西

std::list<Person>* foo = new std::list<Person>();

它看起来像C#和Java泛型,它会做你认为它应该做的事情,但在幕后发生了不同的事情.

它与C#泛型的最大共同点在于它构建了特殊的pseudo-classes,而不是像java那样只是丢弃类型信息,但这是完全不同的一回事.

C#和Java都会产生为虚拟机设计的输出.如果你写了一些包含Person个类的代码,在这两种情况下,关于Person个类的一些信息都会进入.dll或.类文件,JVM/CLR将处理这些.

C++生成原始的x86二进制代码.所有东西都是一个对象,没有底层虚拟机需要了解Person类.没有装箱或拆箱,函数不必属于类,或者任何东西.

Because of this, the C++ compiler places no restrictions on what you can do with templates - basically any code you could write manually, you can get templates to write for you.
The most obvious example is adding things:

在C#和Java中,泛型系统需要知道一个类可以使用哪些方法,并且需要将其传递给虚拟机.告诉它这一点的唯一方法是硬编码实际的类,或者使用接口.例如:

string addNames<T>( T first, T second ) { return first.Name() + second.Name(); }

该代码不能用C#或Java编译,因为它不知道类型T实际上提供了一个名为name()的方法.您必须告诉它-在C#中,就像这样:

interface IHasName{ string Name(); };
string addNames<T>( T first, T second ) where T : IHasName { .... }

然后,您必须确保传递给addNames的内容实现IHasName接口,等等.Java语法不同(<T extends IHasName>),但是它有相同的问题.

这个问题的"classic " case 是try 编写一个函数来实现这一点

string addNames<T>( T first, T second ) { return first + second; }

实际上,您无法编写这段代码,因为没有办法声明一个包含+方法的接口.你失败了.

C++没有这些问题.编译器并不关心将类型传递给任何VM-如果您的两个对象都有.Name()函数,它就会编译.如果他们不这么做,就不会了.很简单.

所以,你有了它:-)

Java相关问答推荐

'在使用apache poi生成Excel数据透视表时,行标签显示为第一列标题,并显示分类汇总

Spring bootstrap @ Asmat注释与@ Routed

try Dockerize Maven应用程序,但发布版本21不支持"

Exe4j创建的应用程序无法再固定在任务栏Windows 11上

在bash中将数组作为Java程序的参数传递

使用Mockito进行的Junit测试失败

具有阻塞方法的开源库是否应该为执行提供异步选项?

Log4j与jdk21兼容吗?

使用用户引入的参数生成人员数组

try 在Android Studio中的infoWindow中使用EditText(Java)

使用正则表达式从字符串中提取多个值

使用While循环打印素数,无法正常工作

Java中不兼容的泛型类型

Java组件项目中的JavaFX对话框国际化

AWS Java SDK v2.x中没有setObjectAcl方法

使用for循环时出现堆栈溢出错误,但如果使用if块执行相同的操作,则不会产生错误

未调用OnBackPressedCallback-Activitiy立即终止

JXBrowser是否支持加载Chrome扩展?

我无法在我的Spring Boot应用程序中导入CSV依赖项

如何使用带有可选参数的类生成器?