我偶尔听说,在使用泛型时,Java 并没有做对
请原谅我的经验不足,但是什么能让他们变得更好呢?
Java 在运行时不强制使用泛型,只在编译时强制使用泛型。
这意味着您可以做一些有趣的事情,比如向泛型集合添加错误的类型。
最大的问题是 Java 泛型只是一个编译时的东西,您可以在运行时颠覆它。C # 之所以受到赞扬,是因为它执行了更多的运行时检查。在 这篇文章中有一些非常好的讨论,并且它链接到其他的讨论。
在 Java 中引入泛型是一项艰巨的任务,因为架构师试图在功能、易用性和向下兼容与遗留代码之间取得平衡。不出所料,各方不得不做出妥协。
还有一些人认为 Java 的泛型实现将语言的复杂性提高到了一个不可接受的水平(参见 Ken Arnold 的“ 泛型被认为是有害的”)。安吉丽卡兰格的 常见问题给出了一个相当好的想法,如何复杂的事情可以成为。
(1)导致一些非常奇怪的行为。我能想到的最好的例子是。假设:
public class MyClass<T> { T getStuff() { ... } List<String> getOtherStuff() { ... } }
然后声明两个变量:
MyClass<T> m1 = ... MyClass m2 = ...
现在拨打 getOtherStuff():
getOtherStuff()
List<String> list1 = m1.getOtherStuff(); List<String> list2 = m2.getOtherStuff();
第二个类型的泛型类型参数被编译器去掉了,因为它是原始类型(意味着没有提供参数化类型) ,尽管它与参数化类型有 没什么的关系。
我还要提到我最喜欢的 JDK 声明:
public class Enum<T extends Enum<T>>
除了通配符(这是一个混合包) ,我只是认为.Net 泛型更好。
主要问题是 Java 在运行时并没有泛型,它只是一个编译时特性。
当你在 Java 中创建一个泛型类时,他们使用一个叫做“类型擦除”的方法从类中移除所有的泛型类型,并且实际上用 Object 替换它们。泛型的最高版本是,编译器只要在方法体中出现指定的泛型类型,就简单地插入对它的转换。
这有很多缺点。恕我直言,最大的问题之一是不能使用反射来检查泛型类型。类型在字节代码中实际上不是泛型,因此不能作为泛型进行检查。
这里对差异有一个很好的概述: http://www.jprl.com/Blog/archive/development/2007/Aug-31.html
在编译时检查 Java 泛型是否正确,然后删除所有类型信息(该进程称为 类型删除)。因此,泛型 List<Integer>将减少到它的 原始类型,非泛型 List,它可以包含任意类的对象。
List<Integer>
List
这样就可以在运行时向列表中插入任意对象,而且现在不可能知道哪些类型被用作泛型参数。后者又导致
ArrayList<Integer> li = new ArrayList<Integer>(); ArrayList<Float> lf = new ArrayList<Float>(); if(li.getClass() == lf.getClass()) // evaluates to true System.out.println("Equal");
坏:
List<byte>
byte[]
好:
Java 泛型仅在编译时被编译成非泛型代码。在 C # 中,实际编译的 MSIL 是通用的。这对性能有巨大的影响,因为 Java 仍然在运行时进行强制转换。更多信息请点击这里.
我希望这是一个维基,这样我就可以添加到其他人... 但是..。
问题:
<
>
它们是编译时而不是运行时的另一个副作用是不能调用泛型类型的构造函数。所以你不能用它们来实现一个通用的工厂..。
public class MyClass { public T getStuff() { return new T(); } }
—— jeffk + +
我要抛出一个非常有争议的观点。泛型使语言复杂化,使代码复杂化。例如,假设我有一个映射,它将一个字符串映射到一个字符串列表。在过去,我可以简单地声明
Map someMap;
现在,我必须声明它是
Map<String, List<String>> someMap;
每次我把它传递给某个方法,我都要重复那个很长的声明。在我看来,所有这些额外的输入会分散开发人员的注意力,使他们脱离“专区”。此外,当代码中充满了大量的“拐点”时,有时很难在以后回过头来快速筛选所有的“拐点”来找到重要的逻辑。
Java 已经因为是最常用的冗长语言之一而臭名昭著,而泛型只是增加了这个问题。
你这么啰嗦,到底想买什么?有多少次你真的遇到过这样的问题: 有人把一个整数放到一个应该包含字符串的集合中,或者有人试图从一个整数集合中拉出一个字符串?在我从事构建商业 Java 应用程序的10年经验中,这从来不是一个大的错误来源。我不太确定你这么啰嗦能得到什么好处。我只是觉得这是个额外的官僚包袱。
现在我要引起争议了。我认为 Java 1.4中的集合最大的问题是需要到处进行类型转换。我认为这些类型转换是额外的、冗长的,与泛型有许多相同的问题。所以,举个例子,我不能
List someList = someMap.get("some key");
我必须这么做
List someList = (List) someMap.get("some key");
当然,原因是 get ()返回一个 Object,它是 List 的超类型。因此,作业不能没有类型转换。再一次,想想这条规则能给你带来多少好处。根据我的经验,没什么。
我认为,如果 Java 1)没有添加泛型,而是2)允许从超类型到子类型的隐式转换,那么 Java 会好得多。在运行时捕获不正确的强制转换。这样我就可以简单地定义
然后再做
所有的漏洞都会消失,而且我真的不认为我会在我的代码中引入一个大的新的漏洞来源。
忽略整个类型擦除混乱,指定的泛型就是不起作用。
结果显示:
List<Integer> x = Collections.emptyList();
但这是一个语法错误:
foo(Collections.emptyList());
Foo 的定义是:
void foo(List<Integer> x) { /* method body not important */ }
因此,表达式类型是否进行检查取决于它是被分配给局部变量还是方法调用的实际参数。多疯狂啊?
如果你听 Java Posse # 279-采访 Joe Darcy 和 Alex Buckley,他们谈论这个问题。这也链接到 Neal Gafter 的一篇题为 Java 的具体泛型的博客文章:
很多人不满意 由路径引起的限制 泛型是用 Java 实现的。 具体来说,他们不满意 泛型类型参数不是 具体化: 它们不在 泛型已实现 使用擦除,其中泛型类型 参数在 运行时间。
那篇博客文章引用了一个较早的条目 通过删除的困惑: 答案部分,它强调了需求中关于 迁移兼容性的观点。
我们的目标是向后提供 源和源的兼容性 对象代码,以及迁移 兼容性。