我偶尔听说过,通用泛型,Java并没有把它弄好.(最近的参考,这里)
原谅我的经验不足,但是什么会让他们变得更好?
坏:
类型信息在编译时丢失,因此在执行时您无法确定它的"含义"类型
不能用于值类型(这是一个大问题 - 在.NET中,List
实际上是由一个byte[]
例子支持,并且不需要装箱)
调用泛型方法的语法很糟糕(IMO)
约束的语法可能会令人困惑
通配符通常令人困惑
由于上述铸造等各种限制
好:
通配符允许在呼叫侧指定协方差/逆变,这在许多情况下非常简洁
它总比没有好!
最大的问题是Java泛型只是一个编译时的东西,你可以在运行时颠覆它.C#受到称赞,因为它进行了更多的运行时检查.这篇文章中有一些非常好的讨论,它与其他讨论有关.
主要问题是Java在运行时实际上没有泛型.这是一个编译时功能.
当您在Java中创建泛型类时,他们使用一个名为"Type Erasure"的方法来实际从类中删除所有泛型类型,并基本上用Object替换它们.泛型版本的泛型是编译器只要在方法体中出现时,只需将强制转换插入指定的泛型类型.
这有很多缺点.恕我直言,最大的一个就是你不能使用反射来检查泛型类型.类型在字节代码中实际上不是通用的,因此不能作为泛型进行检查.
这里的差异很好概述:http://www.jprl.com/Blog/archive/development/2007/Aug-31.html
运行时实现(即不是类型擦除);
使用原始类型的能力(这与(1)有关);
虽然通配符很有用,但语法和知道何时使用它会让很多人感到困惑.和
没有性能提升(因为(1); Java泛型是铸造对象的语法糖).
(1)导致一些非常奇怪的行为.我能想到的最好的例子是.假设:
public class MyClass{ T getStuff() { ... } List getOtherStuff() { ... } }
然后声明两个变量:
MyClassm1 = ... MyClass m2 = ...
现在打电话getOtherStuff()
:
Listlist1 = m1.getOtherStuff(); List list2 = m2.getOtherStuff();
第二个是它的泛型类型参数被编译器剥离,因为它是一个原始类型(意味着没有提供参数化类型),即使它与参数化类型无关.
我还会提到我最喜欢的JDK声明:
public class Enum>
除了通配符(这是一个混合包)我只是认为.Net泛型更好.
我要抛出一个非常有争议的观点.泛型使语言复杂化并使代码复杂化.例如,假设我有一个将字符串映射到字符串列表的映射.在过去,我可以简单地宣布这一点
Map someMap;
现在,我必须声明它
Map> someMap;
每次我把它传递给某种方法时,我都要重复那个重要的长篇宣言.在我看来,所有额外的打字都会分散开发人员的注意力并将他带出"区域".此外,当代码充满了大量的瑕疵时,有时很难再回到它并迅速筛选所有的瑕疵以找到重要的逻辑.
Java作为常用的最冗长的语言之一已经声名狼借,泛型只是增加了这个问题.
那些额外的冗长你真的买了什么?有多少次你有一个问题,有人把一个Integer放入一个应该保存字符串的集合中,或者有人试图从一个整数集合中提取一个字符串?在我10年的构建商业Java应用程序的经验中,这从来就不是错误的重要来源.所以,我不太清楚你为了额外的冗长而得到什么.它真的只是让我感到额外的官僚包袱.
现在我会变得非常有争议.我认为Java 1.4中集合的最大问题是必须在任何地方进行类型转换.我将这些类型转换视为额外的,冗长的,具有许多与泛型相同的问题.所以,例如,我不能这样做
List someList = someMap.get("some key");
我要做
List someList = (List) someMap.get("some key");
当然,原因是get()返回一个Object,它是List的超类型.因此,如果没有类型转换,就无法进行分配.再一次,想一想这个规则真的能给你买多少钱.根据我的经验,并不多.
我认为如果1)它没有添加泛型但是2)允许从超类型到子类型的隐式转换,Java会好一些.在运行时捕获不正确的强制转换.然后我可以有简单的定义
Map someMap;
后来呢
List someList = someMap.get("some key");
所有的残余都将消失,我真的不认为我会在我的代码中引入一个新的大错误来源.
它们的另一个副作用是编译时而不是运行时是你无法调用泛型类型的构造函数.所以你不能用它们来实现通用工厂......
public class MyClass { public T getStuff() { return new T(); } }
--jeffk ++
在编译时检查Java泛型的正确性,然后删除所有类型信息(该过程称为类型擦除.因此,泛型List
将简化为其原始类型,非泛型List
,可包含任意类的对象.
这导致能够在运行时将任意对象插入到列表中,并且现在无法分辨哪些类型被用作通用参数.后者反过来导致
ArrayListli = new ArrayList (); ArrayList lf = new ArrayList (); if(li.getClass() == lf.getClass()) // evaluates to true System.out.println("Equal");
忽略整个类型的擦除混乱,指定的泛型不起作用.
这编译:
Listx = Collections.emptyList();
但这是一个语法错误:
foo(Collections.emptyList());
其中foo定义为:
void foo(Listx) { /* method body not important */ }
因此,表达式类型检查是否取决于它是分配给局部变量还是方法调用的实际参数.这有多疯狂?