Generics Types 泛型学习笔记<三>
作者:冰云
时间:2004-02-29
联系:icecloud(AT)sina.com
Blog:http://icecloud.51.net
真不好意思,这么久才提交上来,有些事情耽搁了。
8 类文字?(Class Literals)作为运行时类型记号(Type Tokens)
在1.5中,java.lang.Class是泛型的,即有一个类型参数T。如String.class,的类型就是Class<String>。这样的好处是,当你使用 reflect构造一个类的时候,可以得到更精确的类别而不是泛泛的Object。
Collection <EmployeeInfo> emps = sqlUtility.select(EmployeeInfo.class, "select name,id from emps"); public static <T> Collection<T> select(Class<T> c, String sqlStatement) { Collection<T> result = new ArrayList<T>(); // run sql query using JDBC for (/* 遍历ResultSet */){ T item = c.newInstance(); /* set all item's fields using reflection*/ result.add(item); } return result; } |
上面的这个select方法适合于所有的类。这样就免去了类型转换。
Note 10: Class作为运行时的记号是个很有用的技巧。在新的annotation API中广泛的应用了这种技术。
原文:This technique of using class literals as run time type tokens is a very useful trick to know. It is used extensively in the new APIs for manipulating annotations.
9 通配符更多的作用
来看下面的例子
public interface Sink<T>{ flush(T t); } public static<T> T writeAll (Collection<T> coll, Sink<T> sink){ T last ; for(T t : coll){ last = t; snk.flush(last); } return last; } Sink<Object> s; Collection<String> cs; String str = writeAll(cs,s); // 非法调用 |
由于调用时,编译器无法确定<T>是什么类型,String还是Object,所以会错误。根据前面的知识,可以使用通配符类型作如下修改:
public static<T> T writeAll (Collection<? extends T> coll, Sink<T> sink) Sink<Object> s; Collection<String> cs; String str = writeAll(cs,s); // 调用合法,但返回值是Object |
<? extends T>,根据Sink<Object>,T是Object类型,返回的T不能安全的给String类型。
这时,就引入了另一种通配符类型:下限通配符(lower bound):<? super T>。
public static<T> T writeAll (Collection<T> coll, Sink<? super T> sink) String str = writeAll(cs,s); // OK |
Note 11: 下限通配符? super T表示一个未知类型是T的超类型,就象? extends T表示未知类型是T的子类型一样。
原文:The solution is to use a form of lower bounded wildcard. The syntax ? super T denotes an unkmown type that is supertype of T. it is the twin of the bounded wildcard we use ? extends T to denote an unknown type that is a subtype of T.
文中后又举了一个例子,是关于Collection中,元素比较大小的方法。例如在TreeSet中,应该提供一个Comparator来比较TreeSet中的元素顺序。并在构造函数中接受。
public interface Comparator<T>{ int compareTo(T fst, T snd); } TreeSet(Comparator<E> c); |
我们提供Comparator<String>可以正常的比较,然而,如果提供Comparator<Object>,也应该正常的工作。因此,TreeSet构造函数应该修改为:
TreeSet(Comparator<? super E> c); |
同样,在Collection中的max方法,返回一个最大值。这些元素必须实现Comparable接口。Collection.max(Comparable<Object>)应该能够工作。而对于 class Foo implements Comparable<Object>,如果提供Comparable<Foo>,应该也能够正常运行。因此,这里也引入下限通配符。
// T必须是实现Comparable的 public static <T extends Comparable<T>> T max(Collection<T> coll); // T还应该能够接受一个实际的类型,所以修改为 public static <T extends Comparable<? super T> T max(Collection<T> 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<String>(); ... public static <T> void addToSet(Set<T> s , T t); addToSet(unknownSet, "abc"); // 非法调用 |
由于unknownSet是未知类型,因此不能接受任何实际的类型。如String。考虑下面的代码:
class Collections{ <T> public static Set<T> unmodifiableSet(Set<T> 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<E> { public boolean containsAll(Collection<E> c); public boolean addAll(Collection<E> c); } |
这是类型安全的,但是并不兼容旧代码。因为范型代码只能接受E类型的Collectioni。
必须考虑到,addAll能够添加任何E的子类型。containsAll也要能够接受不同的类型等等。
第9节提到了max方法。
public static <T extends Comparable<? super T> T max(Collection<T> coll); |
这存在一个问题,就是和旧的代码无法吻合:
public static Comparable max(Collection coll); //新 public static Object max(Collection coll); // 旧 |
通过显式声明一个超类,可以强迫他们一致。并且,我们知道,max仅从他的输入Colelction读取,所以适用于任何T的子类,修改如下:
public static <T 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的一段话作为总结: http://udoo.51.net
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个星期才写完。真是费力啊。感谢各位一直捧场支持。有什么问题欢迎来我
相关推荐
let output = identity<string>("myString"); // type of output will be 'string' ``` 2. 利用类型推论,由编译器根据传入的值自动推断类型: ```typescript let output = identity("myString"); // type of ...
8. 泛型与继承:泛型类可以被其他泛型类或非泛型类继承,这使得我们可以构建复杂的类层次结构,例如 `class Derived<T> : Base<T>`。 9. 泛型与多态:泛型不仅提供了静态类型的安全性,还支持多态性。通过泛型,...
自JDK 1.5以来,Java编程语言引入了一系列重要的扩展功能,其中最重要的一个特性就是泛型(Generics)。本文档旨在向读者介绍Java中的泛型,并通过与类似语言如C++中的模板进行对比,帮助读者理解其相似性和差异性。...
在Java编程语言中,泛型(Generics)是一种强大的特性,它允许我们在编写代码时指定容器(如集合)可以存储的数据类型。这提高了代码的安全性和效率,因为编译器可以在编译时检查类型,避免了运行时...
<br><br>Explore five new chapters of content—including the grammar of the common intermediate language (CIL), .NET generics, and object serialization services. <br>Discover all the new .NET 3.5 ...
在.NET中泛型使用非常频繁,在控制台应用程序中,默认的引入了System.Collection.Generics名称空间,其中就提供了我们经常使用的泛型:List<T>和Dictionary<T>,相信用过它们的都知道它们的强大。还有一种我们经常...
TList<T> 提供了一系列方法,如 Add、Insert、Delete 等,这些方法支持泛型类型的操作。例如: ```delphi procedure TForm1.Button1Click(Sender: TObject); var List: TList<String>; Value: String; begin ...
Java 1.5引入了泛型(Generics)的概念,这是一个重要的语言特性,它允许开发者在编译时期指定集合或其他数据结构中的元素类型,从而避免了运行时期的类型转换错误。通过使用泛型,开发者可以在编程阶段就确保类型的...
These tasks must cooperate to behave correctly, and threads meet the needs of cooperative multitasking.<br><br>Chapter 15Annotationsdescribes the annotation types used to document some of the extra-...
在实例化泛型类时,可以指定具体的类型,如`MyGenericClass<String> instance = new MyGenericClass<>();`。 泛型方法则是在方法签名中使用类型参数,如`public <T> void genericMethod(T t) {...}`。这里,`<T>`...
环境:Windows XP Professional、JDK 1.6、Ant 1.7<br>说明:Java泛型的动机是为解决类型转换在编译时不报错的问题。另外由于“范型编程”(Generic Programming)的推广,于是2004年JDK 5.0引用范型标准。本例子说明...
3. 使用泛型:例如 List<Integer> myIntList = new LinkedList<Integer>()。 泛型的优化: 1. 编译期检查:泛型可以在编译期检查类型的正确性,避免了 ClassCastException。 2. 代码重用:泛型可以将类型参数化,...
Box<String> strBox = new Box<>("Hello, Generics!"); int intValue = intBox.getData(); // 通过泛型类获取数据,无需类型转换 String strValue = strBox.getData(); System.out.println("Integer value: " ...
Java泛型的基本概念是提供一种类型安全的集合类,通过在类名或接口名后面加上尖括号`<>`来指定类型参数。例如,`List<String>`表示一个字符串列表,这里的`String`就是类型参数。泛型可以用于类、接口和方法,使得...
这里,`<T>` 表示这是一个泛型方法,其中 `T` 是类型参数。该方法可以接受任何类型的参数并打印出来。 **3. 泛型的类型参数** 类型参数可以用来定义泛型类或方法的约束。例如,可以限定类型参数必须实现某个接口或...
Java泛型(Generics)是一种在编译时确保类型安全的机制,它允许程序员编写类型安全的通用类或方法,而无需进行显式的类型转换。在Java 1.5引入泛型之前,集合类(如`ArrayList`)只能存储`Object`类型的元素。这...
JDK 1.5的泛型實現(Generics in JDK 1.5) 1 侯捷觀點 JDK 1.5的泛型實現 — Generics in JDK 1.5 — 北京《程序員》 2004/09 台北《Run!PC》2004/09 作者簡介:侯捷,資訊教育、專欄執筆、大學教師...