为什么有人声称 Java 的泛型实现很糟糕?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/520527/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why do some claim that Java's implementation of generics is bad?
提问by sdellysse
回答by Jon Skeet
Bad:
坏的:
- Type information is lost at compile time, so at execution time you can't tell what type it's "meant" to be
- Can't be used for value types (this is a biggie - in .NET a
List<byte>really is backed by abyte[]for example, and no boxing is required) - Syntax for calling generic methods sucks (IMO)
- Syntax for constraints can get confusing
- Wildcarding is generally confusing
- Various restrictions due to the above - casting etc
- 类型信息在编译时丢失,所以在执行时你无法判断它“意味着”是什么类型
- 不能用于值类型(这是一个大问题 - 在 .NET 中,a
List<byte>确实由 a 支持byte[],例如,不需要装箱) - 调用泛型方法的语法很糟糕(IMO)
- 约束的语法可能会令人困惑
- 通配符通常令人困惑
- 由于上述原因的各种限制 - 铸造等
Good:
好的:
- Wildcarding allows covariance/contravariance to be specified at calling side, which is very neat in many situations
- It's better than nothing!
- 通配符允许在调用端指定协方差/逆变,这在很多情况下都非常简洁
- 有总比没有好!
回答by Paul Tomblin
The biggest problem is that Java generics are a compile-time only thing, and you can subvert it at run-time. C# is praised because it does more run-time checking. There is some really good discussion in this post, and it links to other discussions.
最大的问题是 Java 泛型只是编译时的东西,你可以在运行时颠覆它。C# 之所以受到称赞,是因为它做了更多的运行时检查。这篇文章中有一些非常好的讨论,它链接到其他讨论。
回答by JaredPar
The main problem is that Java doesn't actually have generics at runtime. It's a compile time feature.
主要问题是 Java 在运行时实际上没有泛型。这是一个编译时功能。
When you create a generic class in Java they use a method called "Type Erasure" to actually remove all of the generic types from the class and essentially replace them with Object. The mile high version of generics is that the compiler simply inserts casts to the specified generic type whenever it appears in the method body.
当您在 Java 中创建泛型类时,它们使用一种称为“类型擦除”的方法来实际从类中删除所有泛型类型,并基本上用 Object 替换它们。泛型的最高版本是,只要它出现在方法体中,编译器就会简单地将强制转换插入到指定的泛型类型中。
This has a lot of downsides. One of the biggest, IMHO, is that you can't use reflection to inspect a generic type. Types are not actually generic in the byte code and hence can't be inspected as generics.
这有很多缺点。恕我直言,最大的问题之一是您不能使用反射来检查泛型类型。类型在字节码中实际上不是泛型的,因此不能作为泛型进行检查。
Great overview of the differences here: http://www.jprl.com/Blog/archive/development/2007/Aug-31.html
此处差异的大概述:http: //www.jprl.com/Blog/archive/development/2007/Aug-31.html
回答by cletus
- Runtime implementation (ie not type erasure);
- The ability to use primitive types (this is related to (1));
- While the wildcarding is useful the syntax and knowing when to use it is something that stumps a lot of people. and
- No performance improvement (because of (1); Java generics are syntactic sugar for castingi Objects).
- 运行时实现(即不是类型擦除);
- 使用原始类型的能力(这与(1)有关);
- 虽然通配符很有用,但语法和知道何时使用它是难倒很多人的事情。和
- 没有性能提升(因为 (1);Java 泛型是 casti 对象的语法糖)。
(1) leads to some very strange behaviour. The best example I can think of is. Assume:
(1) 导致一些非常奇怪的行为。我能想到的最好的例子是。认为:
public class MyClass<T> {
T getStuff() { ... }
List<String> getOtherStuff() { ... }
}
then declare two variables:
然后声明两个变量:
MyClass<T> m1 = ...
MyClass m2 = ...
Now call getOtherStuff():
现在打电话getOtherStuff():
List<String> list1 = m1.getOtherStuff();
List<String> list2 = m2.getOtherStuff();
The second has its generic type argument stripped off by the compiler because it is a raw type (meaning the parameterized type isn't supplied) even though it has nothingto do with the parameterized type.
第二个具有被编译器剥离的泛型类型参数,因为它是原始类型(意味着未提供参数化类型),即使它与参数化类型无关。
I'll also mention my favourite declaration from the JDK:
我还将提到我最喜欢的 JDK 声明:
public class Enum<T extends Enum<T>>
Apart from wildcarding (which is a mixed bag) I just think the .Net generics are better.
除了通配符(这是一个混合包)我只是认为 .Net 泛型更好。
回答by Clint Miller
I'm going to throw out a really controversial opinion. Generics complicate the language and complicate the code. For example, let's say that I have a map that maps a string to a list of strings. In the old days, I could declare this simply as
我要抛出一个非常有争议的意见。泛型使语言复杂化,使代码复杂化。例如,假设我有一个将字符串映射到字符串列表的映射。在过去,我可以简单地将其声明为
Map someMap;
Now, I have to declare it as
现在,我必须将其声明为
Map<String, List<String>> someMap;
And every time I pass it into some method, I have to repeat that big long declaration all over again. In my opinion, all that extra typing distracts the developer and takes him out of "the zone". Also, when code is filled with lots of cruft, sometimes it's hard to come back to it later and quickly sift through all the cruft to find the important logic.
每次我将它传递给某个方法时,我都必须一遍又一遍地重复那个长长的声明。在我看来,所有额外的打字都会分散开发人员的注意力并将他带出“区域”。此外,当代码中充满了大量杂乱的代码时,有时很难稍后再回到它并快速筛选所有杂乱的代码以找到重要的逻辑。
Java already has a bad reputation for being one of the most verbose languages in common use, and generics just add to that problem.
Java 已经因为是常用的最冗长的语言之一而声名狼藉,而泛型只会加剧这个问题。
And what do you really buy for all that extra verbosity? How many times have you really had problems where someone put an Integer into a collection that's supposed to hold Strings, or where someone tried to pull a String out of a collection of Integers? In my 10 years of experience working at building commercial Java applications, this has just never been a big source of errors. So, I'm not really sure what you're getting for the extra verbosity. It really just strikes me as extra bureaucratic baggage.
对于所有这些额外的冗长,您真正购买的是什么?有多少次您真的遇到问题,有人将一个整数放入一个应该保存字符串的集合中,或者有人试图从一个整数集合中取出一个字符串?在我 10 年构建商业 Java 应用程序的工作经验中,这从来都不是错误的主要来源。所以,我不确定你会因为额外的冗长而得到什么。这真的只是让我觉得是额外的官僚包袱。
Now I'm going to get really controversial. What I see as the biggest problem with collections in Java 1.4 is the necessity to typecast everywhere. I view those typecasts as extra, verbose cruft that have many of the same problems as generics. So, for example, I can't just do
现在我会变得非常有争议。我认为 Java 1.4 中集合的最大问题是必须在任何地方进行类型转换。我认为这些类型转换是多余的、冗长的杂物,它们与泛型存在许多相同的问题。所以,例如,我不能只是做
List someList = someMap.get("some key");
I have to do
我要做
List someList = (List) someMap.get("some key");
The reason, of course, is that get() returns an Object which is a supertype of List. So the assignment can't be made without a typecast. Again, think about how much that rule really buys you. From my experience, not much.
原因当然是 get() 返回一个对象,它是 List 的超类型。因此,没有类型转换就无法进行分配。再一次,想一想这条规则到底能给你带来多少好处。根据我的经验,不多。
I think Java would have been way better off if 1) it had not added generics but 2) instead had allowed implicit casting from a supertype to a subtype. Let incorrect casts be caught at runtime. Then I could have had the simplicity of defining
我认为如果 1)Java 没有添加泛型,而是 2)允许从超类型隐式转换为子类型,Java 会更好。让不正确的转换在运行时被捕获。然后我就可以简单地定义
Map someMap;
and later doing
后来做
List someList = someMap.get("some key");
all the cruft would be gone, and I really don't think I'd be introducing a big new source of bugs into my code.
所有的杂物都会消失,我真的不认为我会在我的代码中引入一大堆新的错误来源。
回答by jdkoftinoff
Another side effect of them being compile-time and not run time is that you can't call the constructor of the generic type. So you can't use them to implement a generic factory...
它们是编译时而非运行时的另一个副作用是您无法调用泛型类型的构造函数。所以你不能用它们来实现一个通用工厂......
public class MyClass {
public T getStuff() {
return new T();
}
}
--jeffk++
--jeffk++
回答by Anton Gogolev
Java generics are checked for correctness at compile time and then all type information is removed (the process is called type erasure. Thus, generic List<Integer>will be reduced to its raw type, non-generic List, which can contain objects of arbitrary class.
在编译时检查 Java 泛型的正确性,然后删除所有类型信息(该过程称为类型擦除。因此,泛型List<Integer>将减少为其原始类型,非泛型List,它可以包含任意类的对象。
This results in being able to insert arbitrary objects to the list at runtime, as well as it's now impossible to tell what types were used as generic parameters. The latter in turn results in
这导致能够在运行时将任意对象插入到列表中,并且现在无法判断哪些类型被用作泛型参数。后者反过来导致
ArrayList<Integer> li = new ArrayList<Integer>();
ArrayList<Float> lf = new ArrayList<Float>();
if(li.getClass() == lf.getClass()) // evaluates to true
System.out.println("Equal");
回答by Nat
Ignoring the whole type erasure mess, generics as specified just don't work.
忽略整个类型擦除混乱,指定的泛型不起作用。
This compiles:
这编译:
List<Integer> x = Collections.emptyList();
But this is a syntax error:
但这是一个语法错误:
foo(Collections.emptyList());
Where foo is defined as:
其中 foo 定义为:
void foo(List<Integer> x) { /* method body not important */ }
So whether an expression type checks depends on whether it is being assigned to a local variable or an actual parameter of a method call. How crazy is that?
因此,表达式类型是否检查取决于它是否被分配给局部变量或方法调用的实际参数。那有多疯狂?
回答by Laplie Anderson
I wish this was a wiki so I could add to other people... but...
我希望这是一个维基,这样我就可以添加给其他人……但是……
Problems:
问题:
- Type Erasure (no runtime availability)
- No support for primative types
- Incompatability with Annotations (they were both added in 1.5 I'm still not sure why annotations don't allow generics aside from rushing the features)
- Incompatability with Arrays. (Sometimes I really want to do somthing like Class
<? extends MyObject>[], but I'm not allowed) - Wierd wildcard syntax and behavior
- The fact that generic support is inconsistant across Java classes. They added it to most of the collections methods, but every once in a while, you run into an instance where its not there.
- 类型擦除(无运行时可用性)
- 不支持原始类型
- 与注释不兼容(它们都是在 1.5 中添加的,我仍然不确定为什么注释除了匆忙功能之外不允许使用泛型)
- 与数组不兼容。(有时我真的很想做 Class 之类的事情
<?扩展 MyObject>[],但我不允许) - 奇怪的通配符语法和行为
- 泛型支持在 Java 类中不一致的事实。他们将它添加到大多数集合方法中,但每隔一段时间,你就会遇到一个它不存在的实例。
回答by Zach Scrivena
The introduction of generics into Java was a difficult task because the architects were trying to balance functionality, ease of use, and backward compatibility with legacy code. Quite expectedly, compromises had to be made.
将泛型引入 Java 是一项艰巨的任务,因为架构师试图平衡功能、易用性以及与遗留代码的向后兼容性。不出所料,必须做出妥协。
There are some who also feel that Java's implementation of generics increased the complexity of the language to an unacceptable level (see Ken Arnold's "Generics Considered Harmful"). Angelika Langer's Generics FAQsgives a pretty good idea as to how complicated things can become.
有些人还认为 Java 对泛型的实现将语言的复杂性增加到了无法接受的程度(参见 Ken Arnold 的“泛型被认为有害”)。Angelika Langer 的泛型常见问题解答提供了一个很好的主意,说明事情会变得多么复杂。

