我主要使用Java,泛型相对较新.我一直读到Java做出了错误的决定.NET有更好的实现等等.
那么,C++、C#和Java在泛型上的主要区别是什么呢?各有优劣吗?
我主要使用Java,泛型相对较新.我一直读到Java做出了错误的决定.NET有更好的实现等等.
那么,C++、C#和Java在泛型上的主要区别是什么呢?各有优劣吗?
我会加入我的声音,try 让事情变得清晰:
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时才会发生这种情况
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
的列表,而不是任何其他数组列表.
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()函数,它就会编译.如果他们不这么做,就不会了.很简单.
所以,你有了它:-)