I mostly use Java and generics are relatively new. I keep reading that Java made the wrong decision or that .NET has better implementations etc. etc.
我主要使用Java,而泛型是相对较新的。我一直读到Java做了错误的决定或者。net有更好的实现等等。
So, what are the main differences between C++, C#, Java in generics? Pros/cons of each?
那么,在泛型中,c++、c#、Java的主要区别是什么?每一个的优点/缺点?
13 个解决方案
#1
365
I'll add my voice to the noise and take a stab at making things clear:
我会把我的声音加入到噪音中,试着把事情弄清楚:
C# Generics allow you to declare something like this.
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
.
然后编译器会阻止你把不是人的东西放入列表中。在幕后,c#编译器只是将List
The benefit of this is that it makes it really fast. There's no casting or any other stuff, and because the dll contains the information that this is a List of Person
, other code that looks at it later on using reflection can tell that it contains Person
objects (so you get intellisense and so on).
这样做的好处是,它能让它变得非常快。不存在强制类型转换或任何其他东西,而且因为dll包含这是一个Person列表的信息,所以稍后使用反射查看它的其他代码可以告诉它包含Person对象(因此您可以获得intellisense等)。
The downside of this is that old C# 1.0 and 1.1 code (before they added generics) doesn't understand these new List<something>
, so you have to manually convert things back to plain old List
to interoperate with them. This is not that big of a problem, because C# 2.0 binary code is not backwards compatible. The only time this will ever happen is if you're upgrading some old C# 1.0/1.1 code to C# 2.0
它的缺点是旧的c# 1.0和1.1代码(在添加泛型之前)不理解这些新列表< >,所以您必须手动将内容转换回普通的旧列表以与它们进行互操作。这并不是什么大问题,因为c# 2.0二进制代码不是向后兼容的。这种情况只会发生在您将旧的c# 1.0/1.1代码升级到c# 2.0时
Java Generics allow you to declare something like this.
ArrayList<Person> foo = new ArrayList<Person>();
On the surface it looks the same, and it sort-of is. The compiler will also prevent you from putting things that aren't Person
into the list.
表面上看起来是一样的。编译器还将阻止您将不属于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
不同之处在于幕后发生的事情。与c#不同的是,Java并不去构建一个特殊的ListOfPerson—它只使用Java中一直存在的普通旧的ArrayList。当你从数组中取出东西时,通常的Person p = (Person)foo.get(1);跳投舞仍需完成。编译器正在为你保存按键,但是速度的影响仍然和以前一样。当人们提到“类型擦除”时,这就是他们谈论的。编译器为您插入数据类型转换,然后“删除”它应该是一个人列表而不仅仅是对象的事实
The benefit of this approach is that old code which doesn't understand generics doesn't have to care. It's still dealing with the same old ArrayList
as it always has. This is more important in the java world because they wanted to support compiling code using Java 5 with generics, and having it run on old 1.4 or previous JVM's, which microsoft deliberately decided not to bother with.
这种方法的好处是不理解泛型的旧代码不必在意。它仍然和以前一样处理相同的ArrayList。在java世界中,这一点更为重要,因为他们希望支持使用java 5编译带有泛型的代码,并让它在旧的1.4或以前的JVM上运行,而微软故意不去理会JVM。
The downside is the speed hit I mentioned previously, and also because there is no ListOfPerson
pseudo-class or anything like that going into the .class files, code that looks at it later on (with reflection, or if you pull it out of another collection where it's been converted into Object
or so on) can't tell in any way that it's meant to be a list containing only Person
and not just any other array list.
缺点是速度达到我前面提到的,也因为没有ListOfPerson伪类或类似的东西进入. class文件,代码看起来在它之后(反射,或者如果你把它从一个集合被改造成对象或等)不能以任何方式告诉它应该只包含列表的人,而不仅仅是任何其他数组列表。
C++ Templates allow you to declare something like this
std::list<Person>* foo = new std::list<Person>();
It looks like C# and Java generics, and it will do what you think it should do, but behind the scenes different things are happening.
它看起来像c#和Java泛型,它将做您认为它应该做的事情,但是在幕后,不同的事情正在发生。
It has the most in common with C# generics in that it builds special pseudo-classes
rather than just throwing the type information away like java does, but it's a whole different kettle of fish.
它与c#泛型最大的共同之处在于,它构建了特殊的伪类,而不是像java那样简单地丢弃类型信息,但这是一个完全不同的问题。
Both C# and Java produce output which is designed for virtual machines. If you write some code which has a Person
class in it, in both cases some information about a Person
class will go into the .dll or .class file, and the JVM/CLR will do stuff with this.
c#和Java都生成为虚拟机设计的输出。如果您编写一些包含Person类的代码,在这两种情况下,关于Person类的一些信息将进入.dll或.class文件,JVM/CLR将对此进行处理。
C++ produces raw x86 binary code. Everything is not an object, and there's no underlying virtual machine which needs to know about a Person
class. There's no boxing or unboxing, and functions don't have to belong to classes, or indeed anything.
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++编译器对如何使用模板没有任何限制——基本上你可以手工编写任何代码,你可以让模板为你编写。最明显的例子就是添加东西:
In C# and Java, the generics system needs to know what methods are available for a class, and it needs to pass this down to the virtual machine. The only way to tell it this is by either hard-coding the actual class in, or using interfaces. For example:
在c#和Java中,泛型系统需要知道类可以使用哪些方法,并且需要将这些方法传递给虚拟机。唯一的方法是硬编码实际的类,或者使用接口。例如:
string addNames<T>( T first, T second ) { return first.Name() + second.Name(); }
That code won't compile in C# or Java, because it doesn't know that the type T
actually provides a method called Name(). You have to tell it - in C# like this:
该代码不会在c#或Java中编译,因为它不知道类型T实际上提供了一个名为Name()的方法。你必须这样说——在c#中:
interface IHasName{ string Name(); };
string addNames<T>( T first, T second ) where T : IHasName { .... }
And then you have to make sure the things you pass to addNames implement the IHasName interface and so on. The java syntax is different (<T extends IHasName>
), but it suffers from the same problems.
然后你必须确保你传递给addNames的东西实现了IHasName接口等等。java语法是不同的(
The 'classic' case for this problem is trying to write a function which does this
这个问题的“经典”情况是尝试编写一个函数来实现这一点
string addNames<T>( T first, T second ) { return first + second; }
You can't actually write this code because there are no ways to declare an interface with the +
method in it. You fail.
实际上,您无法编写此代码,因为没有方法声明带有+方法的接口。你失败了。
C++ suffers from none of these problems. The compiler doesn't care about passing types down to any VM's - if both your objects have a .Name() function, it will compile. If they don't, it won't. Simple.
c++不存在这些问题。编译器并不关心将类型传递给任何VM——如果两个对象都有. name()函数,它将编译。如果他们不这样做,就不会。简单。
So, there you have it :-)
所以,你有了:-)
#2
61
C++ rarely uses the “generics” terminology. Instead, the word “templates” is used and is more accurate. Templates describes one technique to achieve a generic design.
c++很少使用“泛型”术语。相反,使用“模板”一词更准确。模板描述了一种实现通用设计的技术。
C++ templates is very different from what both C# and Java implement for two main reasons. The first reason is that C++ templates don't only allow compile-time type arguments but also compile-time const-value arguments: templates can be given as integers or even function signatures. This means that you can do some quite funky stuff at compile time, e.g. calculations:
由于两个主要原因,c++模板与c#和Java实现的模板非常不同。第一个原因是c++模板不仅允许编译时类型参数,而且还允许编译时的conner -value参数:模板可以作为整数或函数签名提供。这意味着你可以在编译时做一些非常有趣的事情,例如计算:
template <unsigned int N>
struct product {
static unsigned int const VALUE = N * product<N - 1>::VALUE;
};
template <>
struct product<1> {
static unsigned int const VALUE = 1;
};
// Usage:
unsigned int const p5 = product<5>::VALUE;
This code also uses the other distinguished feature of C++ templates, namely template specialization. The code defines one class template, product
that has one value argument. It also defines a specialization for that template that is used whenever the argument evaluates to 1. This allows me to define a recursion over template definitions. I believe that this was first discovered by Andrei Alexandrescu.
这段代码还使用了c++模板的另一个特性,即模板专门化。该代码定义了一个类模板,即具有一个值参数的产品。它还定义了在参数计算为1时使用的模板的专门化。这允许我在模板定义上定义递归。我相信这是安德烈·亚历山大最先发现的。
Template specialization is important for C++ because it allows for structural differences in data structures. Templates as a whole is a means of unifying an interface across types. However, although this is desirable, all types cannot be treated equally inside the implementation. C++ templates takes this into account. This is very much the same difference that OOP makes between interface and implementation with the overriding of virtual methods.
模板专门化对于c++非常重要,因为它允许数据结构中的结构差异。模板作为一个整体是一种跨类型统一接口的方法。然而,尽管这是可取的,但是在实现中所有类型都不能被同等对待。c++模板考虑到了这一点。这与OOP在接口和实现之间与虚拟方法覆盖的区别非常相似。
C++ templates are essential for its algorithmic programming paradigm. For example, almost all algorithms for containers are defined as functions that accept the container type as a template type and treat them uniformly. Actually, that's not quite right: C++ doesn't work on containers but rather on ranges that are defined by two iterators, pointing to the beginning and behind the end of the container. Thus, the whole content is circumscribed by the iterators: begin <= elements < end.
c++模板对于它的算法编程范例是必不可少的。例如,几乎所有容器的算法都定义为接受容器类型为模板类型并统一处理它们的函数。实际上,这并不完全正确:c++不支持容器,而是支持由两个迭代器定义的范围,它们指向容器的开始和结束。因此,整个内容被迭代器限制:开始<=元素< end。
Using iterators instead of containers is useful because it allows to operate on parts of a container instead of on the whole.
使用迭代器而不是容器是有用的,因为它允许对容器的部分而不是整个容器进行操作。
Another distinguishing feature of C++ is the possibility of partial specialization for class templates. This is somewhat related to pattern matching on arguments in Haskell and other functional languages. For example, let's consider a class that stores elements:
c++的另一个显著特性是对类模板进行部分专门化的可能性。这与Haskell和其他函数语言中的参数的模式匹配有关。例如,让我们考虑一个存储元素的类:
template <typename T>
class Store { … }; // (1)
This works for any element type. But let's say that we can store pointers more effciently than other types by applying some special trick. We can do this by partially specializing for all pointer types:
这适用于任何元素类型。但是,假设我们可以通过应用一些特殊的技巧来更有效地存储指针。我们可以对所有指针类型进行部分专门化:
template <typename T>
class Store<T*> { … }; // (2)
Now, whenever we instance a container template for one type, the appropriate definition is used:
现在,每当我们为一种类型实例一个容器模板时,都会使用适当的定义:
Store<int> x; // Uses (1)
Store<int*> y; // Uses (2)
Store<string**> z; // Uses (2), with T = string*.
#3
35
Anders Hejlsberg himself described the differences here "Generics in C#, Java, and C++".
Anders Hejlsberg自己描述了这里的差异“c#、Java和c++中的泛型”。
#4
18
There are already a lot of good answers on what the differences are, so let me give a slightly different perspective and add the why.
已经有很多关于差异的好答案了,所以让我给出一个稍微不同的观点,并加上原因。
As was already explained, the main difference is type erasure, i.e. the fact that the Java compiler erases the generic types and they don't end up in the generated bytecode. However, the question is: why would anyone do that? It doesn't make sense! Or does it?
如前所述,主要的区别是类型擦除,即Java编译器擦除泛型类型,并且它们最终不会出现在生成的字节码中。然而,问题是:为什么会有人这么做?它没有意义!还是它?
Well, what's the alternative? If you don't implement generics in the language, where do you implement them? And the answer is: in the Virtual Machine. Which breaks backwards compatibility.
嗯,有什么选择?如果您没有在语言中实现泛型,那么在哪里实现它们呢?答案是:在虚拟机中。打破向后兼容性。
Type erasure, on the other hand, allows you to mix generic clients with non-generic libraries. In other words: code that was compiled on Java 5 can still be deployed to Java 1.4.
另一方面,类型擦除允许您将泛型客户端与非泛型库混合使用。换句话说:在Java 5上编译的代码仍然可以部署到Java 1.4中。
Microsoft, however, decided to break backwards compatibility for generics. That's why .NET Generics are "better" than Java Generics.
然而,微软决定打破泛型的向后兼容性。这就是为什么。net泛型比Java泛型要好。
Of course, Sun aren't idiots or cowards. The reason why they "chickened out", was that Java was significantly older and more widespread than .NET when they introduced generics. (They were introduced roughly at the same time in both worlds.) Breaking backwards compatibility would have been a huge pain.
当然,Sun不是白痴或懦夫。他们“害怕”的原因是,当他们引入泛型时,Java比。net更古老,更广泛。(它们是在两个世界大致同时被引入的。)打破向后的兼容性将是一个巨大的痛苦。
Put yet another way: in Java, Generics are a part of the Language (which means they apply only to Java, not to other languages), in .NET they are part of the Virtual Machine (which means they apply to all languages, not just C# and Visual Basic.NET).
换句话说:在Java中,泛型是语言的一部分(这意味着它们只适用于Java,而不适用于其他语言),在。net中,泛型是虚拟机的一部分(这意味着它们适用于所有语言,而不仅仅是c#和Visual Basic.NET)。
Compare this with .NET features like LINQ, lambda expressions, local variable type inference, anonymous types and expression trees: these are all language features. That's why there are subtle differences between VB.NET and C#: if those features were part of the VM, they would be the same in all languages. But the CLR hasn't changed: it's still the same in .NET 3.5 SP1 as it was in .NET 2.0. You can compile a C# program that uses LINQ with the .NET 3.5 compiler and still run it on .NET 2.0, provided that you don't use any .NET 3.5 libraries. That would not work with generics and .NET 1.1, but it would work with Java and Java 1.4.
比较一下。net特性,比如LINQ, lambda表达式,局部变量类型推断,匿名类型和表达式树:这些都是语言特性。这就是为什么VB有细微的区别。NET和c#:如果这些特性是VM的一部分,那么它们在所有语言中都是一样的。但是CLR并没有改变:它在。net 3.5 SP1中仍然和在。net 2.0中一样。如果不使用任何。net 3.5库,您可以编译一个c#程序,使用。net 3.5编译器的LINQ,并在。net 2.0上运行它。这在泛型和。net 1.1中是行不通的,但是在Java和Java 1.4中是行得通的。
#5
14
Follow-up to my previous posting.
跟进我之前的帖子。
Templates are one of the main reasons why C++ fails so abysmally at intellisense, regardless of the IDE used. Because of template specialization, the IDE can never be really sure if a given member exists or not. Consider:
模板是导致c++在intellisense上如此糟糕地失败的主要原因之一,而不管使用的IDE是什么。由于模板专门化,IDE永远无法真正确定给定的成员是否存在。考虑:
template <typename T>
struct X {
void foo() { }
};
template <>
struct X<int> { };
typedef int my_int_type;
X<my_int_type> a;
a.|
Now, the cursor is at the indicated position and it's damn hard for the IDE to say at that point if, and what, members a
has. For other languages the parsing would be straightforward but for C++, quite a bit of evaluation is needed beforehand.
现在,光标在指定的位置,对于IDE来说,在那一点说如果,以及什么,成员a有。对于其他语言,解析很简单,但是对于c++,需要进行相当多的评估。
It gets worse. What if my_int_type
were defined inside a class template as well? Now its type would depend on another type argument. And here, even compilers fail.
它变得更糟。如果在类模板中也定义了my_int_type呢?现在它的类型取决于另一个类型参数。在这里,即使编译器也会失败。
template <typename T>
struct Y {
typedef T my_type;
};
X<Y<int>::my_type> b;
After a bit of thinking, a programmer would conclude that this code is the same as the above: Y<int>::my_type
resolves to int
, therefore b
should be the same type as a
, right?
经过一番思考,程序员会得出这样的结论:这段代码与上面的代码相同:Y
Wrong. At the point where the compiler tries to resolve this statement, it doesn't actually know Y<int>::my_type
yet! Therefore, it doesn't know that this is a type. It could be something else, e.g. a member function or a field. This might give rise to ambiguities (though not in the present case), therefore the compiler fails. We have to tell it explicitly that we refer to a type name:
错了。当编译器试图解析这个语句时,它实际上还不知道Y
X<typename Y<int>::my_type> b;
Now, the code compiles. To see how ambiguities arise from this situation, consider the following code:
现在,代码编译。要了解这种情况是如何产生歧义的,请考虑以下代码:
Y<int>::my_type(123);
This code statement is perfectly valid and tells C++ to execute the function call to Y<int>::my_type
. However, if my_type
is not a function but rather a type, this statement would still be valid and perform a special cast (the function-style cast) which is often a constructor invocation. The compiler can't tell which we mean so we have to disambiguate here.
这个代码语句是完全有效的,它告诉c++执行对Y
#6
6
Both Java and C# introduced generics after their first language release. However, there are differences in how the core libraries changed when generics was introduced. C#'s generics are not just compiler magic and so it was not possible to generify existing library classes without breaking backwards compatibility.
Java和c#在它们的第一个语言版本之后都引入了泛型。然而,当引入泛型时,核心库的变化方式存在差异。c#的泛型不仅仅是编译器的魔法,所以不可能在不破坏向后兼容性的情况下对现有库类进行泛型。
For example, in Java the existing Collections Framework was completely genericised. Java does not have both a generic and legacy non-generic version of the collections classes. In some ways this is much cleaner - if you need to use a collection in C# there is really very little reason to go with the non-generic version, but those legacy classes remain in place, cluttering up the landscape.
例如,在Java中,现有的集合框架是完全一般化的。Java没有集合类的泛型版本和遗留的非泛型版本。在某些方面,这是非常干净的——如果您需要在c#中使用一个集合,那么就没有理由去使用非泛型版本,但是那些遗留类仍然保留在那里,使整个场景混乱不堪。
Another notable difference is the Enum classes in Java and C#. Java's Enum has this somewhat tortuous looking definition:
另一个显著的区别是Java和c#中的Enum类。Java的Enum有一个有点曲折的定义:
// java.lang.Enum Definition in Java
public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable {
(see Angelika Langer's very clear explanation of exactly why this is so. Essentially, this means Java can give type safe access from a string to its Enum value:
(参见Angelika Langer非常清楚地解释了为什么会这样。本质上,这意味着Java可以将类型安全访问从字符串传递到枚举值:
// Parsing String to Enum in Java
Colour colour = Colour.valueOf("RED");
Compare this to C#'s version:
与c#的版本相比:
// Parsing String to Enum in C#
Colour colour = (Colour)Enum.Parse(typeof(Colour), "RED");
As Enum already existed in C# before generics was introduced to the language, the definition could not change without breaking existing code. So, like collections, it remains in the core libraries in this legacy state.
由于在将泛型引入语言之前,c#中已经存在Enum,因此如果不破坏现有代码,定义就无法更改。因此,与集合一样,它仍然保留在这个遗留状态的核心库中。
#7
4
11 months late, but I think this question is ready for some Java Wildcard stuff.
11个月过去了,但是我认为这个问题已经为一些Java通配符准备好了。
This is a syntactical feature of Java. Suppose you have a method:
这是Java的语法特性。假设你有一个方法:
public <T> void Foo(Collection<T> thing)
And suppose you don't need to refer to the type T in the method body. You're declaring a name T and then only using it once, so why should you have to think of a name for it? Instead, you can write:
假设您不需要在方法体中引用类型T。你声明一个名字T,然后只使用一次,为什么你要为它想出一个名字呢?相反,你可以写:
public void Foo(Collection<?> thing)
The question-mark asks the the compiler to pretend that you declared a normal named type parameter that only needs to appear once in that spot.
问题标记要求编译器假设您声明了一个普通的命名类型参数,该参数只需要出现一次。
There's nothing you can do with wildcards that you can't also do with a named type parameter (which is how these things are always done in C++ and C#).
对于通配符,您可以使用命名类型参数(这是在c++和c#中经常使用的方法)进行操作。
#8
2
Wikipedia has great write-ups comparing both Java/C# generics and Java generics/C++ templates. The main article on Generics seems a bit cluttered but it does have some good info in it.
Wikipedia对Java/ c#泛型和Java泛型/ c++模板进行了很好的比较。关于泛型的主要文章似乎有点杂乱,但其中确实有一些很好的信息。
#9
1
The biggest complaint is type erasure. In that, generics are not enforced at runtime. Here's a link to some Sun docs on the subject.
最大的抱怨是类型删除。在这种情况下,泛型不会在运行时强制执行。这里有一个关于这个主题的Sun文档的链接。
Generics are implemented by type erasure: generic type information is present only at compile time, after which it is erased by the compiler.
泛型通过类型擦除实现:泛型类型信息只在编译时出现,之后被编译器删除。
#10
1
C++ templates are actually much more powerful than their C# and Java counterparts as they are evaluated at compile time and support specialization. This allows for Template Meta-Programming and makes the C++ compiler equivalent to a Turing machine (i.e. during the compilation process you can compute anything that is computable with a Turing machine).
c++模板实际上比c#和Java模板强大得多,因为它们在编译时进行评估,并支持专门化。这允许模板元编程,并使c++编译器与图灵机等价(例如,在编译过程中,您可以用图灵机计算任何可计算的东西)。
#11
1
In Java, generics are compiler level only, so you get:
在Java中,泛型仅是编译器级别的,因此可以得到:
a = new ArrayList<String>()
a.getClass() => ArrayList
Note that the type of 'a' is an array list, not a list of strings. So the type of a list of bananas would equal() a list of monkeys.
注意,“a”的类型是数组列表,而不是字符串列表。所以香蕉清单的类型就等于()猴子清单。
So to speak.
可以这么说。
#12
1
Looks like, among other very interesting proposals, there is one about refining generics and breaking backwards compatibility:
看起来,在其他非常有趣的提议中,有一个是关于改进泛型和打破向后兼容性的:
Currently, generics are implemented using erasure, which means that the generic type information is not available at runtime, which makes some kind of code hard to write. Generics were implemented this way to support backwards compatibility with older non-generic code. Reified generics would make the generic type information available at runtime, which would break legacy non-generic code. However, Neal Gafter has proposed making types reifiable only if specified, so as to not break backward compatibility.
目前,泛型是使用擦除来实现的,这意味着泛型类型信息在运行时不可用,这使得一些代码难以编写。泛型是通过这种方式实现的,以支持与旧的非泛型代码的向后兼容。具体化泛型将使泛型类型信息在运行时可用,这将破坏遗留的非泛型代码。然而,Neal Gafter提出只有在指定的情况下才可以使类型具体化,以避免破坏向后兼容性。
at Alex Miller's article about Java 7 Proposals
在Alex Miller关于Java 7的文章中。
#13
0
NB: I don't have enough point to comment, so feel free to move this as a comment to appropriate answer.
NB:我没有足够的理由来评论,所以你可以把这句话作为适当的回答。
Contrary to popular believe, which I never understand where it came from, .net implemented true generics without breaking backward compatibility, and they spent explicit effort for that. You don't have to change your non-generic .net 1.0 code into generics just to be used in .net 2.0. Both the generic and non-generic lists are still available in .Net framework 2.0 even until 4.0, exactly for nothing else but backward compatibility reason. Therefore old codes that still used non-generic ArrayList will still work, and use the same ArrayList class as before. Backward code compatibility is always maintained since 1.0 till now... So even in .net 4.0, you still have to option to use any non-generics class from 1.0 BCL if you choose to do so.
与流行的观点相反,我从来都不知道它是从哪里来的,net实现了真正的泛型,而不破坏向后兼容性,他们为此付出了明显的努力。不需要将非通用的。net 1.0代码改为泛型,只需要在。net 2.0中使用。在。net framework 2.0中,直到4.0之前,通用和非通用列表都是可用的,这完全是出于向后兼容的原因。因此,仍然使用非泛型ArrayList的旧代码仍然可以工作,并使用与以前相同的ArrayList类。从1.0到现在,一直保持向后代码兼容性……因此,即使在。net 4.0中,如果您选择使用1.0 BCL中的非泛型类,您仍然需要选择。
So I don't think java has to break backward compatibility to support true generics.
所以我不认为java必须打破向后兼容性来支持真正的泛型。
#1
365
I'll add my voice to the noise and take a stab at making things clear:
我会把我的声音加入到噪音中,试着把事情弄清楚:
C# Generics allow you to declare something like this.
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
.
然后编译器会阻止你把不是人的东西放入列表中。在幕后,c#编译器只是将List
The benefit of this is that it makes it really fast. There's no casting or any other stuff, and because the dll contains the information that this is a List of Person
, other code that looks at it later on using reflection can tell that it contains Person
objects (so you get intellisense and so on).
这样做的好处是,它能让它变得非常快。不存在强制类型转换或任何其他东西,而且因为dll包含这是一个Person列表的信息,所以稍后使用反射查看它的其他代码可以告诉它包含Person对象(因此您可以获得intellisense等)。
The downside of this is that old C# 1.0 and 1.1 code (before they added generics) doesn't understand these new List<something>
, so you have to manually convert things back to plain old List
to interoperate with them. This is not that big of a problem, because C# 2.0 binary code is not backwards compatible. The only time this will ever happen is if you're upgrading some old C# 1.0/1.1 code to C# 2.0
它的缺点是旧的c# 1.0和1.1代码(在添加泛型之前)不理解这些新列表< >,所以您必须手动将内容转换回普通的旧列表以与它们进行互操作。这并不是什么大问题,因为c# 2.0二进制代码不是向后兼容的。这种情况只会发生在您将旧的c# 1.0/1.1代码升级到c# 2.0时
Java Generics allow you to declare something like this.
ArrayList<Person> foo = new ArrayList<Person>();
On the surface it looks the same, and it sort-of is. The compiler will also prevent you from putting things that aren't Person
into the list.
表面上看起来是一样的。编译器还将阻止您将不属于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
不同之处在于幕后发生的事情。与c#不同的是,Java并不去构建一个特殊的ListOfPerson—它只使用Java中一直存在的普通旧的ArrayList。当你从数组中取出东西时,通常的Person p = (Person)foo.get(1);跳投舞仍需完成。编译器正在为你保存按键,但是速度的影响仍然和以前一样。当人们提到“类型擦除”时,这就是他们谈论的。编译器为您插入数据类型转换,然后“删除”它应该是一个人列表而不仅仅是对象的事实
The benefit of this approach is that old code which doesn't understand generics doesn't have to care. It's still dealing with the same old ArrayList
as it always has. This is more important in the java world because they wanted to support compiling code using Java 5 with generics, and having it run on old 1.4 or previous JVM's, which microsoft deliberately decided not to bother with.
这种方法的好处是不理解泛型的旧代码不必在意。它仍然和以前一样处理相同的ArrayList。在java世界中,这一点更为重要,因为他们希望支持使用java 5编译带有泛型的代码,并让它在旧的1.4或以前的JVM上运行,而微软故意不去理会JVM。
The downside is the speed hit I mentioned previously, and also because there is no ListOfPerson
pseudo-class or anything like that going into the .class files, code that looks at it later on (with reflection, or if you pull it out of another collection where it's been converted into Object
or so on) can't tell in any way that it's meant to be a list containing only Person
and not just any other array list.
缺点是速度达到我前面提到的,也因为没有ListOfPerson伪类或类似的东西进入. class文件,代码看起来在它之后(反射,或者如果你把它从一个集合被改造成对象或等)不能以任何方式告诉它应该只包含列表的人,而不仅仅是任何其他数组列表。
C++ Templates allow you to declare something like this
std::list<Person>* foo = new std::list<Person>();
It looks like C# and Java generics, and it will do what you think it should do, but behind the scenes different things are happening.
它看起来像c#和Java泛型,它将做您认为它应该做的事情,但是在幕后,不同的事情正在发生。
It has the most in common with C# generics in that it builds special pseudo-classes
rather than just throwing the type information away like java does, but it's a whole different kettle of fish.
它与c#泛型最大的共同之处在于,它构建了特殊的伪类,而不是像java那样简单地丢弃类型信息,但这是一个完全不同的问题。
Both C# and Java produce output which is designed for virtual machines. If you write some code which has a Person
class in it, in both cases some information about a Person
class will go into the .dll or .class file, and the JVM/CLR will do stuff with this.
c#和Java都生成为虚拟机设计的输出。如果您编写一些包含Person类的代码,在这两种情况下,关于Person类的一些信息将进入.dll或.class文件,JVM/CLR将对此进行处理。
C++ produces raw x86 binary code. Everything is not an object, and there's no underlying virtual machine which needs to know about a Person
class. There's no boxing or unboxing, and functions don't have to belong to classes, or indeed anything.
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++编译器对如何使用模板没有任何限制——基本上你可以手工编写任何代码,你可以让模板为你编写。最明显的例子就是添加东西:
In C# and Java, the generics system needs to know what methods are available for a class, and it needs to pass this down to the virtual machine. The only way to tell it this is by either hard-coding the actual class in, or using interfaces. For example:
在c#和Java中,泛型系统需要知道类可以使用哪些方法,并且需要将这些方法传递给虚拟机。唯一的方法是硬编码实际的类,或者使用接口。例如:
string addNames<T>( T first, T second ) { return first.Name() + second.Name(); }
That code won't compile in C# or Java, because it doesn't know that the type T
actually provides a method called Name(). You have to tell it - in C# like this:
该代码不会在c#或Java中编译,因为它不知道类型T实际上提供了一个名为Name()的方法。你必须这样说——在c#中:
interface IHasName{ string Name(); };
string addNames<T>( T first, T second ) where T : IHasName { .... }
And then you have to make sure the things you pass to addNames implement the IHasName interface and so on. The java syntax is different (<T extends IHasName>
), but it suffers from the same problems.
然后你必须确保你传递给addNames的东西实现了IHasName接口等等。java语法是不同的(
The 'classic' case for this problem is trying to write a function which does this
这个问题的“经典”情况是尝试编写一个函数来实现这一点
string addNames<T>( T first, T second ) { return first + second; }
You can't actually write this code because there are no ways to declare an interface with the +
method in it. You fail.
实际上,您无法编写此代码,因为没有方法声明带有+方法的接口。你失败了。
C++ suffers from none of these problems. The compiler doesn't care about passing types down to any VM's - if both your objects have a .Name() function, it will compile. If they don't, it won't. Simple.
c++不存在这些问题。编译器并不关心将类型传递给任何VM——如果两个对象都有. name()函数,它将编译。如果他们不这样做,就不会。简单。
So, there you have it :-)
所以,你有了:-)
#2
61
C++ rarely uses the “generics” terminology. Instead, the word “templates” is used and is more accurate. Templates describes one technique to achieve a generic design.
c++很少使用“泛型”术语。相反,使用“模板”一词更准确。模板描述了一种实现通用设计的技术。
C++ templates is very different from what both C# and Java implement for two main reasons. The first reason is that C++ templates don't only allow compile-time type arguments but also compile-time const-value arguments: templates can be given as integers or even function signatures. This means that you can do some quite funky stuff at compile time, e.g. calculations:
由于两个主要原因,c++模板与c#和Java实现的模板非常不同。第一个原因是c++模板不仅允许编译时类型参数,而且还允许编译时的conner -value参数:模板可以作为整数或函数签名提供。这意味着你可以在编译时做一些非常有趣的事情,例如计算:
template <unsigned int N>
struct product {
static unsigned int const VALUE = N * product<N - 1>::VALUE;
};
template <>
struct product<1> {
static unsigned int const VALUE = 1;
};
// Usage:
unsigned int const p5 = product<5>::VALUE;
This code also uses the other distinguished feature of C++ templates, namely template specialization. The code defines one class template, product
that has one value argument. It also defines a specialization for that template that is used whenever the argument evaluates to 1. This allows me to define a recursion over template definitions. I believe that this was first discovered by Andrei Alexandrescu.
这段代码还使用了c++模板的另一个特性,即模板专门化。该代码定义了一个类模板,即具有一个值参数的产品。它还定义了在参数计算为1时使用的模板的专门化。这允许我在模板定义上定义递归。我相信这是安德烈·亚历山大最先发现的。
Template specialization is important for C++ because it allows for structural differences in data structures. Templates as a whole is a means of unifying an interface across types. However, although this is desirable, all types cannot be treated equally inside the implementation. C++ templates takes this into account. This is very much the same difference that OOP makes between interface and implementation with the overriding of virtual methods.
模板专门化对于c++非常重要,因为它允许数据结构中的结构差异。模板作为一个整体是一种跨类型统一接口的方法。然而,尽管这是可取的,但是在实现中所有类型都不能被同等对待。c++模板考虑到了这一点。这与OOP在接口和实现之间与虚拟方法覆盖的区别非常相似。
C++ templates are essential for its algorithmic programming paradigm. For example, almost all algorithms for containers are defined as functions that accept the container type as a template type and treat them uniformly. Actually, that's not quite right: C++ doesn't work on containers but rather on ranges that are defined by two iterators, pointing to the beginning and behind the end of the container. Thus, the whole content is circumscribed by the iterators: begin <= elements < end.
c++模板对于它的算法编程范例是必不可少的。例如,几乎所有容器的算法都定义为接受容器类型为模板类型并统一处理它们的函数。实际上,这并不完全正确:c++不支持容器,而是支持由两个迭代器定义的范围,它们指向容器的开始和结束。因此,整个内容被迭代器限制:开始<=元素< end。
Using iterators instead of containers is useful because it allows to operate on parts of a container instead of on the whole.
使用迭代器而不是容器是有用的,因为它允许对容器的部分而不是整个容器进行操作。
Another distinguishing feature of C++ is the possibility of partial specialization for class templates. This is somewhat related to pattern matching on arguments in Haskell and other functional languages. For example, let's consider a class that stores elements:
c++的另一个显著特性是对类模板进行部分专门化的可能性。这与Haskell和其他函数语言中的参数的模式匹配有关。例如,让我们考虑一个存储元素的类:
template <typename T>
class Store { … }; // (1)
This works for any element type. But let's say that we can store pointers more effciently than other types by applying some special trick. We can do this by partially specializing for all pointer types:
这适用于任何元素类型。但是,假设我们可以通过应用一些特殊的技巧来更有效地存储指针。我们可以对所有指针类型进行部分专门化:
template <typename T>
class Store<T*> { … }; // (2)
Now, whenever we instance a container template for one type, the appropriate definition is used:
现在,每当我们为一种类型实例一个容器模板时,都会使用适当的定义:
Store<int> x; // Uses (1)
Store<int*> y; // Uses (2)
Store<string**> z; // Uses (2), with T = string*.
#3
35
Anders Hejlsberg himself described the differences here "Generics in C#, Java, and C++".
Anders Hejlsberg自己描述了这里的差异“c#、Java和c++中的泛型”。
#4
18
There are already a lot of good answers on what the differences are, so let me give a slightly different perspective and add the why.
已经有很多关于差异的好答案了,所以让我给出一个稍微不同的观点,并加上原因。
As was already explained, the main difference is type erasure, i.e. the fact that the Java compiler erases the generic types and they don't end up in the generated bytecode. However, the question is: why would anyone do that? It doesn't make sense! Or does it?
如前所述,主要的区别是类型擦除,即Java编译器擦除泛型类型,并且它们最终不会出现在生成的字节码中。然而,问题是:为什么会有人这么做?它没有意义!还是它?
Well, what's the alternative? If you don't implement generics in the language, where do you implement them? And the answer is: in the Virtual Machine. Which breaks backwards compatibility.
嗯,有什么选择?如果您没有在语言中实现泛型,那么在哪里实现它们呢?答案是:在虚拟机中。打破向后兼容性。
Type erasure, on the other hand, allows you to mix generic clients with non-generic libraries. In other words: code that was compiled on Java 5 can still be deployed to Java 1.4.
另一方面,类型擦除允许您将泛型客户端与非泛型库混合使用。换句话说:在Java 5上编译的代码仍然可以部署到Java 1.4中。
Microsoft, however, decided to break backwards compatibility for generics. That's why .NET Generics are "better" than Java Generics.
然而,微软决定打破泛型的向后兼容性。这就是为什么。net泛型比Java泛型要好。
Of course, Sun aren't idiots or cowards. The reason why they "chickened out", was that Java was significantly older and more widespread than .NET when they introduced generics. (They were introduced roughly at the same time in both worlds.) Breaking backwards compatibility would have been a huge pain.
当然,Sun不是白痴或懦夫。他们“害怕”的原因是,当他们引入泛型时,Java比。net更古老,更广泛。(它们是在两个世界大致同时被引入的。)打破向后的兼容性将是一个巨大的痛苦。
Put yet another way: in Java, Generics are a part of the Language (which means they apply only to Java, not to other languages), in .NET they are part of the Virtual Machine (which means they apply to all languages, not just C# and Visual Basic.NET).
换句话说:在Java中,泛型是语言的一部分(这意味着它们只适用于Java,而不适用于其他语言),在。net中,泛型是虚拟机的一部分(这意味着它们适用于所有语言,而不仅仅是c#和Visual Basic.NET)。
Compare this with .NET features like LINQ, lambda expressions, local variable type inference, anonymous types and expression trees: these are all language features. That's why there are subtle differences between VB.NET and C#: if those features were part of the VM, they would be the same in all languages. But the CLR hasn't changed: it's still the same in .NET 3.5 SP1 as it was in .NET 2.0. You can compile a C# program that uses LINQ with the .NET 3.5 compiler and still run it on .NET 2.0, provided that you don't use any .NET 3.5 libraries. That would not work with generics and .NET 1.1, but it would work with Java and Java 1.4.
比较一下。net特性,比如LINQ, lambda表达式,局部变量类型推断,匿名类型和表达式树:这些都是语言特性。这就是为什么VB有细微的区别。NET和c#:如果这些特性是VM的一部分,那么它们在所有语言中都是一样的。但是CLR并没有改变:它在。net 3.5 SP1中仍然和在。net 2.0中一样。如果不使用任何。net 3.5库,您可以编译一个c#程序,使用。net 3.5编译器的LINQ,并在。net 2.0上运行它。这在泛型和。net 1.1中是行不通的,但是在Java和Java 1.4中是行得通的。
#5
14
Follow-up to my previous posting.
跟进我之前的帖子。
Templates are one of the main reasons why C++ fails so abysmally at intellisense, regardless of the IDE used. Because of template specialization, the IDE can never be really sure if a given member exists or not. Consider:
模板是导致c++在intellisense上如此糟糕地失败的主要原因之一,而不管使用的IDE是什么。由于模板专门化,IDE永远无法真正确定给定的成员是否存在。考虑:
template <typename T>
struct X {
void foo() { }
};
template <>
struct X<int> { };
typedef int my_int_type;
X<my_int_type> a;
a.|
Now, the cursor is at the indicated position and it's damn hard for the IDE to say at that point if, and what, members a
has. For other languages the parsing would be straightforward but for C++, quite a bit of evaluation is needed beforehand.
现在,光标在指定的位置,对于IDE来说,在那一点说如果,以及什么,成员a有。对于其他语言,解析很简单,但是对于c++,需要进行相当多的评估。
It gets worse. What if my_int_type
were defined inside a class template as well? Now its type would depend on another type argument. And here, even compilers fail.
它变得更糟。如果在类模板中也定义了my_int_type呢?现在它的类型取决于另一个类型参数。在这里,即使编译器也会失败。
template <typename T>
struct Y {
typedef T my_type;
};
X<Y<int>::my_type> b;
After a bit of thinking, a programmer would conclude that this code is the same as the above: Y<int>::my_type
resolves to int
, therefore b
should be the same type as a
, right?
经过一番思考,程序员会得出这样的结论:这段代码与上面的代码相同:Y
Wrong. At the point where the compiler tries to resolve this statement, it doesn't actually know Y<int>::my_type
yet! Therefore, it doesn't know that this is a type. It could be something else, e.g. a member function or a field. This might give rise to ambiguities (though not in the present case), therefore the compiler fails. We have to tell it explicitly that we refer to a type name:
错了。当编译器试图解析这个语句时,它实际上还不知道Y
X<typename Y<int>::my_type> b;
Now, the code compiles. To see how ambiguities arise from this situation, consider the following code:
现在,代码编译。要了解这种情况是如何产生歧义的,请考虑以下代码:
Y<int>::my_type(123);
This code statement is perfectly valid and tells C++ to execute the function call to Y<int>::my_type
. However, if my_type
is not a function but rather a type, this statement would still be valid and perform a special cast (the function-style cast) which is often a constructor invocation. The compiler can't tell which we mean so we have to disambiguate here.
这个代码语句是完全有效的,它告诉c++执行对Y
#6
6
Both Java and C# introduced generics after their first language release. However, there are differences in how the core libraries changed when generics was introduced. C#'s generics are not just compiler magic and so it was not possible to generify existing library classes without breaking backwards compatibility.
Java和c#在它们的第一个语言版本之后都引入了泛型。然而,当引入泛型时,核心库的变化方式存在差异。c#的泛型不仅仅是编译器的魔法,所以不可能在不破坏向后兼容性的情况下对现有库类进行泛型。
For example, in Java the existing Collections Framework was completely genericised. Java does not have both a generic and legacy non-generic version of the collections classes. In some ways this is much cleaner - if you need to use a collection in C# there is really very little reason to go with the non-generic version, but those legacy classes remain in place, cluttering up the landscape.
例如,在Java中,现有的集合框架是完全一般化的。Java没有集合类的泛型版本和遗留的非泛型版本。在某些方面,这是非常干净的——如果您需要在c#中使用一个集合,那么就没有理由去使用非泛型版本,但是那些遗留类仍然保留在那里,使整个场景混乱不堪。
Another notable difference is the Enum classes in Java and C#. Java's Enum has this somewhat tortuous looking definition:
另一个显著的区别是Java和c#中的Enum类。Java的Enum有一个有点曲折的定义:
// java.lang.Enum Definition in Java
public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable {
(see Angelika Langer's very clear explanation of exactly why this is so. Essentially, this means Java can give type safe access from a string to its Enum value:
(参见Angelika Langer非常清楚地解释了为什么会这样。本质上,这意味着Java可以将类型安全访问从字符串传递到枚举值:
// Parsing String to Enum in Java
Colour colour = Colour.valueOf("RED");
Compare this to C#'s version:
与c#的版本相比:
// Parsing String to Enum in C#
Colour colour = (Colour)Enum.Parse(typeof(Colour), "RED");
As Enum already existed in C# before generics was introduced to the language, the definition could not change without breaking existing code. So, like collections, it remains in the core libraries in this legacy state.
由于在将泛型引入语言之前,c#中已经存在Enum,因此如果不破坏现有代码,定义就无法更改。因此,与集合一样,它仍然保留在这个遗留状态的核心库中。
#7
4
11 months late, but I think this question is ready for some Java Wildcard stuff.
11个月过去了,但是我认为这个问题已经为一些Java通配符准备好了。
This is a syntactical feature of Java. Suppose you have a method:
这是Java的语法特性。假设你有一个方法:
public <T> void Foo(Collection<T> thing)
And suppose you don't need to refer to the type T in the method body. You're declaring a name T and then only using it once, so why should you have to think of a name for it? Instead, you can write:
假设您不需要在方法体中引用类型T。你声明一个名字T,然后只使用一次,为什么你要为它想出一个名字呢?相反,你可以写:
public void Foo(Collection<?> thing)
The question-mark asks the the compiler to pretend that you declared a normal named type parameter that only needs to appear once in that spot.
问题标记要求编译器假设您声明了一个普通的命名类型参数,该参数只需要出现一次。
There's nothing you can do with wildcards that you can't also do with a named type parameter (which is how these things are always done in C++ and C#).
对于通配符,您可以使用命名类型参数(这是在c++和c#中经常使用的方法)进行操作。
#8
2
Wikipedia has great write-ups comparing both Java/C# generics and Java generics/C++ templates. The main article on Generics seems a bit cluttered but it does have some good info in it.
Wikipedia对Java/ c#泛型和Java泛型/ c++模板进行了很好的比较。关于泛型的主要文章似乎有点杂乱,但其中确实有一些很好的信息。
#9
1
The biggest complaint is type erasure. In that, generics are not enforced at runtime. Here's a link to some Sun docs on the subject.
最大的抱怨是类型删除。在这种情况下,泛型不会在运行时强制执行。这里有一个关于这个主题的Sun文档的链接。
Generics are implemented by type erasure: generic type information is present only at compile time, after which it is erased by the compiler.
泛型通过类型擦除实现:泛型类型信息只在编译时出现,之后被编译器删除。
#10
1
C++ templates are actually much more powerful than their C# and Java counterparts as they are evaluated at compile time and support specialization. This allows for Template Meta-Programming and makes the C++ compiler equivalent to a Turing machine (i.e. during the compilation process you can compute anything that is computable with a Turing machine).
c++模板实际上比c#和Java模板强大得多,因为它们在编译时进行评估,并支持专门化。这允许模板元编程,并使c++编译器与图灵机等价(例如,在编译过程中,您可以用图灵机计算任何可计算的东西)。
#11
1
In Java, generics are compiler level only, so you get:
在Java中,泛型仅是编译器级别的,因此可以得到:
a = new ArrayList<String>()
a.getClass() => ArrayList
Note that the type of 'a' is an array list, not a list of strings. So the type of a list of bananas would equal() a list of monkeys.
注意,“a”的类型是数组列表,而不是字符串列表。所以香蕉清单的类型就等于()猴子清单。
So to speak.
可以这么说。
#12
1
Looks like, among other very interesting proposals, there is one about refining generics and breaking backwards compatibility:
看起来,在其他非常有趣的提议中,有一个是关于改进泛型和打破向后兼容性的:
Currently, generics are implemented using erasure, which means that the generic type information is not available at runtime, which makes some kind of code hard to write. Generics were implemented this way to support backwards compatibility with older non-generic code. Reified generics would make the generic type information available at runtime, which would break legacy non-generic code. However, Neal Gafter has proposed making types reifiable only if specified, so as to not break backward compatibility.
目前,泛型是使用擦除来实现的,这意味着泛型类型信息在运行时不可用,这使得一些代码难以编写。泛型是通过这种方式实现的,以支持与旧的非泛型代码的向后兼容。具体化泛型将使泛型类型信息在运行时可用,这将破坏遗留的非泛型代码。然而,Neal Gafter提出只有在指定的情况下才可以使类型具体化,以避免破坏向后兼容性。
at Alex Miller's article about Java 7 Proposals
在Alex Miller关于Java 7的文章中。
#13
0
NB: I don't have enough point to comment, so feel free to move this as a comment to appropriate answer.
NB:我没有足够的理由来评论,所以你可以把这句话作为适当的回答。
Contrary to popular believe, which I never understand where it came from, .net implemented true generics without breaking backward compatibility, and they spent explicit effort for that. You don't have to change your non-generic .net 1.0 code into generics just to be used in .net 2.0. Both the generic and non-generic lists are still available in .Net framework 2.0 even until 4.0, exactly for nothing else but backward compatibility reason. Therefore old codes that still used non-generic ArrayList will still work, and use the same ArrayList class as before. Backward code compatibility is always maintained since 1.0 till now... So even in .net 4.0, you still have to option to use any non-generics class from 1.0 BCL if you choose to do so.
与流行的观点相反,我从来都不知道它是从哪里来的,net实现了真正的泛型,而不破坏向后兼容性,他们为此付出了明显的努力。不需要将非通用的。net 1.0代码改为泛型,只需要在。net 2.0中使用。在。net framework 2.0中,直到4.0之前,通用和非通用列表都是可用的,这完全是出于向后兼容的原因。因此,仍然使用非泛型ArrayList的旧代码仍然可以工作,并使用与以前相同的ArrayList类。从1.0到现在,一直保持向后代码兼容性……因此,即使在。net 4.0中,如果您选择使用1.0 BCL中的非泛型类,您仍然需要选择。
So I don't think java has to break backward compatibility to support true generics.
所以我不认为java必须打破向后兼容性来支持真正的泛型。