, 如果提供 Comparable ,应该也能够正常运行。因此,这里也引入下限通配符。
// T 必须是实现 Comparable 的
public static extends Comparable >
T max (Collection coll);
// T 还应该能够接受一个实际的类型,所以修改为
public static extends Comparable super T>
T max (Collection coll);
那么,如何正确的使用通配符呢?
Note 12: 一般的,如果你的 API 仅仅使用类型参数 T 作为参数,它应该利用下限通配符;相反,如果你的 API 返回 T ,你应该用上限通配符给你的客户端更多地便利。
原文: In general if you have an API that only uses a type parameter T as an argument, its uses should take advantage of lower bounded wildcard. Conversely, if the API only returns T, you ’ ll give your clients more flexibility by using upper bounded wildcards.
通配符捕获, Wildcard capture
下面的例子应该已经很明确的不能执行:
Set unknownSet = new HashSet ();
...
public static void addToSet (Set s , T t);
addToSet (unknownSet, "abc" ); // 非法调用
由于unknownSet 是未知类型,因此不能接受任何实际的类型。如String 。考虑下面的代码:
class Collections{
public static Set unmodifiableSet (Set set);
}
Set s = Collections. unmodifiableSet (unknownSet); // works!
看起来这段代码不应该被允许,但是它是合法的。这是因为通配符捕获原则。
Note 13: 这种情形出现的很频繁,因而有一个特殊的规则允许这样的代码出现,这被证明是安全的:通配符捕获,允许编译器判断未知通配符类型作为类型参数的范型方法。通配符捕获仅仅允许那些在方法参数列表中出现一次的类型参数。
原文: Because this situation arises relatively frequently, there is a special rule that allows such code under very specific circumstances in which the code can be proven to be safe. This rule, known as wildcard capture , allows the compiler to infer the unknown type of a wildcard as a type argument to a generic method. Wildcard capture is only allowed if the type parameter being inferred only appears once in the methods argument list.
10 转换旧代码到范型
这称之为范型化(generifying )。如果要转换旧代码到范型,请仔细的考虑如何修改。如java.util.Collection :
public interface Collection {
public boolean containsAll (Collection c);
public boolean addAll (Collection c);
}
public interface Collection {
public boolean containsAll (Collection c);
public boolean addAll (Collection c);
}
这是类型安全的,但是并不兼容旧代码。因为范型代码只能接受E 类型的 Collectioni 。
必须考虑到,addAll 能够添加任何E 的子类型。containsAll 也要能够接受不同的类型等等。
第9 节提到了max 方法。
public static extends Comparable super T>
T max (Collection coll);
这存在一个问题,就是和旧的代码无法吻合:
public static Comparable max (Collection coll); // 新
public static Object max (Collection coll); // 旧
通过显式声明一个超类,可以强迫他们一致。并且,我们知道,max 仅从他的输入Colelction 读取,所以适用于任何T 的子类,修改如下:
public static extends Object & Comparable super T>
T max (Collection extends T> coll);
这种情况比较少见,但是如果设计一个library ,就应该准备认真考虑转换他们。
Note 14: 转换一套既有的 API 时,你必须确定范型 API 不会过度的限制,并且继续支持原来的 API 。。
原文: When converting existing APIs, you should think carefully to make certain that the generic API is not unduly restrictive and continue to support the original contract of the API.
另一种需要当心的问题是:返回值协变(covariant returns )。即改进(refine )子类方法的返回值。旧代码如下:
public class Foo {
public Foo create) {} // 工厂方法
}
public class Bar extends Foo {
public Foo create () {} // 实际上建立的 Bar
}
考虑返回值协变的优点:
public class Foo {
public Foo create) {} // 工厂方法
}
public class Bar extends Foo {
public Bar create () {} // 实际上建立的 Bar
}
Note 13: 这种特性并不被 JVM 直接支持,而是被编译器所支持。
原文: The JVM does not directly support overriding of methods with different return types. This feature is supported by the compiler.
在此我引用我朋友udoo 的一段话作为总结:
Java 1.5 中很重要的部分就是 generics 。 java generics 的诞生大概与 .net 的逼迫有关系,之前的多个版本从未看到有如此大的变化。
在 java 这种强类型的语言中, type casting 是很难控制的一个事情,编译器可以在编译时解决一部分,在运行时刻遇到这种问题,是程序潜在的隐患。 reflection 技术是解决应用的灵活配置的一个重要手段,但只能在运行时刻才能知道究竟是哪个类,可能会有 casting 的问题。
C++ 中的模板技术的目的是提供快捷、易于使用的工具箱, STL 是 c++ 编程必须掌握的技术。 java generics 看起来与 c++ 模板很象,但通过这篇文章我们知道, java generics 更着重在类型检查上,可以去掉看起来很难猜测的强制类型转换,大概只有程序编写者自己最清楚到底这里是什么类型。使用 java generics ,程序可能会更好读一些,也降低了运行时刻的风险,但这里提到的几个概念和技术,却也并不易于理解(文中也有几处笔误)。 theserverside.com 对于是否需要这种技术有争论,不管怎样,又给我们提供了一个手段,需要的时候是用的上的。
花了 3 天看完的,花了 1 个星期才写完。真是费力啊。感谢各位一直捧场支持。有什么问题欢迎来我 blog 讨论.
版权声明:
本文由冰云完成,首发于CSDN ,作者保留中文版权。
未经许可,不得使用于任何商业用途。
欢迎转载,但请保持文章及版权声明完整。
如需联络请发邮件:icecloud(AT)sina.com