------- android培训、java培训、期待与您交流! ----------
什么是泛型?
泛型(Generic type 或者 generics)是对 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类。可以把类型参数看作是使用参数化类型时指定的类型的一个占位符,就像方法的形式参数是运行时传递的值的占位符一样。
可以在集合框架(Collection framework)中看到泛型的动机。例如,Map 类允许向一个 Map 添加任意类的对象,即使最常见的情况是在给定映射(map)中保存某个特定类型(比如 String)的对象。因为 Map.get() 被定义为返回 Object,所以一般必须将 Map.get() 的结果强制类型转换为期望的类型:
Map m = new HashMap();
m.put("key", "blarg");
String s = (String) m.get("key");
要让程序通过编译,必须将 get() 的结果强制类型转换为 String,并且希望结果真的是一个 String。但是有可能某人已经在该映射中保存了不是 String 的东西,这样的话,上面的代码将会抛出 ClassCastException。
泛型的好处
Java 语言中引入泛型是一个较大的功能增强。不仅语言、类型系统和编译器有了较大的变化,以支持泛型,而且类库也进行了大翻修,所以许多重要的类,比如集合框架,都已经成为泛型化的了。这带来了很多好处:
类型安全。 泛型的主要目标是提高 Java 程序的类型安全。通过知道使用泛型定义的变量的类型限制,编译器可以在一个高得多的程度上验证类型假设。没有泛型,这些假设就只存在于程序员的头脑中(或者如果幸运的话,还存在于代码注释中)。
Java 程序中的一种流行技术是定义这样的集合,即它的元素或键是公共类型的,比如“String 列表”或者“String 到 String 的映射”。通过在变量声明中捕获这一附加的类型信息,泛型允许编译器实施这些附加的类型约束。类型错误现在就可以在编译时被捕获了,而不是在运行时当作 ClassCastException 展示出来。将类型检查从运行时挪到编译时有助于您更容易找到错误,并可提高程序的可靠性。
消除强制类型转换。 泛型的一个附带好处是,消除源代码中的许多强制类型转换。这使得代码更加可读,并且减少了出错机会。
泛型类中的类型参数几乎可以用于任何可以使用类名的地方。
例如,下面是 java.util.Map 接口的定义的摘录:
public interface Map<K, V> {
public void put(K key, V value);
public V get(K key);
}
附:推荐名称
K —— 键,比如映射的键。
V —— 值,比如 List 和 Set 的内容,或者 Map 中的值。
E —— 异常类。
T —— 泛型。
泛型不是协变的
关于泛型的混淆,一个常见的来源就是假设它们像数组一样是协变的。其实它们不是协变的。List<Object> 不是 List<String> 的父类型。
如果 A 扩展 B,那么 A 的数组也是 B 的数组,并且完全可以在需要 B[] 的地方使用 A[]:
Integer[] intArray = new Integer[10];
Number[] numberArray = intArray;
下面的代码是无效的:
List<Integer> intList = new ArrayList<Integer>();
List<Number> numberList = intList; // invalid
-->再如,下面的这个方法只接受List<Object>, 类似List<Integer>都不行:
(如果把参数改成原始类型,即List,则只收到警告信息)
void printList(List<Object> l) {
for (Object o : l)
System.out.println(o);
}
-->解决方案是使用类型通配符:
void printList(List<?> l) {
for (Object o : l)
System.out.println(o);
}
“?”是一个类型通配符。List<?> 是任何泛型 List 的父类型,所以完全可以将 List<Object>、List<Integer> 或 List<List<List<Flutzpah>>> 传递给 printList()。
-->如此一来,下面的代码则工作得很好(调用 List.get() 并推断返回类型为Object):
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(42));
List<?> lu = li;
System.out.println(lu.get(0));
但是对于这句代码:
lu.add(new Integer(43)); // Error,是错误的。
∵编译器不能对 List 的类型参数作出足够严密的推理,以确定将 Integer 传递给 List.add() 是类型安全的。所以编译器将不允许这么做。
对于:
lu.clear();// OK,这是可以的。
∵它不依赖于编译器必须知道关于 lu 的类型参数的任何信息。
特别注意:如果<Bar>,可就当心了!
public class Bar extends Foo {
public Foo create(){
return new Bar();
}
}
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
不能实例化类型变量(类型擦除erasure)
不能使用像new T(...)、new T[...]或T.class这样的表达式中的类型变量。
泛型类的静态上下文中不能使用类型变量
public class Singleton<T> {
private static T singleInstance;//ERROR
public static T getSingleInstance(){...}//ERROR
}
∵类型参数T是属于具体实例的。一个类所有泛型都对应着同一个class,static成员只属于class。
泛型类被所有其实例(instances)共享的另一个暗示是检查一个实例是不是一个特定类型的泛型类是没有意义的。
Collection cs = new ArrayList<String>();
if (cs instanceof Collection<String>) { ...} // 非法
类似的,如下的类型转换
Collection<String> cstr = (Collection<String>) cs;
得到一个unchecked warning,因为运行时环境不会为你作这样的检查。