java 泛型Generics

import java.util.*;
public class Generics {
	//无限制的通配符类型
	static int numElementsInCommon(Set<?> s1, Set<?> s2) {
		int result = 0;
		for (Object o : s1)
			if (s2.contains(o))
				result++;
		return result;
	}

	private Map<Class<?>, Object> favorites =
			new HashMap<Class<?>, Object>();
	public <T> void putFavorite(Class<T> type, T instance) {
		if (type == null)
			throw new NullPointerException("Type is null");
		favorites.put(type, instance);
	}
	//泛型方法
	public <T> T getFavorite(Class<T> type) {
		return type.cast(favorites.get(type));
	}
	public static void main(String[] args) {
		Generics g = new Generics();
		g.putFavorite(String.class, "Java");
		g.putFavorite(Integer.class, 100086);
		g.putFavorite(Class.class, Generics.class);
		String favoriteString = g.getFavorite(String.class);
		int favoriteInteger = g.getFavorite(Integer.class);
		Class<?> favoriteClass = g.getFavorite(Class.class);
		System.out.printf("%s %d %s%n", favoriteString,
				favoriteInteger, favoriteClass.getSimpleName());

		Pair p = new Pair<Object> (23, "skidoo");
		System.out.println(p.first() + " " + p.last());
		//编译错误:因为Pair p被声明为原生类型,擦除了所有的<T>类型信息,stringList()方法返回值类型为List,而不是List<String>。
		//for (String s : p.stringList())
		for (Object s : p.stringList())
			System.out.print(s + " ");
	}
}
class Pair<T> {
 	private final T first;
 	private final T last;
	public Pair(T first, T last) {
		this.first = first;
		this.last = last;
 	}
 	public T first() {
  		return first;
 	}
 	public T last() {
  		return last;
 	}
 	public List<String> stringList() {
  		return Arrays.asList(String.valueOf(first),
    			String.valueOf(last));
 	}
}

//泛型类的内部类可以访问到其外围类的类型参数。
//当你在一个泛型类中嵌套另一个泛型类时,最好为它们的类型参数设置不同的名字,即使那个嵌套类是静态的也应如此。
class LinkedList<E> {
 	
 	/* 访问外围类的类型参数
   	private Node head = null;
 	private class Node {
 	   	E value;
   	   	Node next;
   	   	Node(E value, Node next) {
   	   	   	this.value = value;
   	   	   	this.next = next;
   	   	   	head = this;
   	   	}
   	}
   	public void add(E e) {
   	   	new Node(e);
   	}
   	public void dump() {
   	   	for (Node n = head; n != null; n = n.next)
   	   	   	System.out.print(n.value + " ");
   	}
   	*/
   	//使用静态类
   	private Node<E> head = null;
   	private static class Node<T> {
      	   	T value;
      	   	Node<T> next;
      	   	Node(T value, Node<T> next) {
       	   	   	this.value = value;
       	   	   	this.next = next;
      	   	}
     	}
     	public void add(E e) {
      	   	head = new Node<E>(e, head);
     	}
     	public void dump() {
      	   	for (Node<E> n = head; n != null; n = n.next)
       	   	   	System.out.print(n.value + " ");
     	}
}

 * java1.5增加了泛型(Generics)

   泛型通用参数命名规则:T表示任意的类型,E表示结合的元素类型,K和V表示映射的键和值类型,

   X表示异常,任何类型的序列可以是T、U、V或者T1、T2、T3。

 * 泛型的作用:在编译时检查类型转换,而不是在运行时发现类型转换错误。

   Java中的泛型只是作用于代码编译阶段,在编译过程中,对于正确检验泛型结果后,会将泛型的相关信息

   擦除,也就是说,编译后的class文件中是不包含任何泛型信息的。泛型信息不会进入到运行时阶段。

 * 请不要在新代码中使用原生态类型:
   如果使用原生态类型就失去了泛型在安全性和表述性方面的所有优势。

   使用原生态的例外情况:Class类必须使用原生态类型(List.class);instanceof操作符必须使用原生态

   类型(obj instanceof List)。

 * 泛型有子类型化(subtyping)的规则。List<String>是原生态类型List的一个子类型,不是List<Object>

   的子类型。

 * 列表优先于数组:
   数组和泛型相比有两个重要的不同点:
   (1) 数组是协变式的(covariant),如果Sub是Super的子类型,那么Sub[]也是Super[]的子类型。
        相反,泛型是不可变的(invariant),对于任意两个不同的类型Type1和Type2,List<Type1>既不会

        是List<Type2>的子类型,也不会是超类型。

   (2) 数组是具体化的,因此数组会在运行时才知道并检查元素的类型约束。
        泛型是通过擦除(erasure)来实现的,泛型只在编译时强化它们的类型信息,并在运行时丢弃元素

        类型信息,使泛型可以与没有使用泛型的代码随意互用。
   由于上述这些根本的区别,数组和泛型不能很好地混合使用。

   new E[100]              //编译错误:can not create a generics array of T

   解决的办法:创建一个Object数组并将它转换成泛型数组类型。 (E[]) new Object[capacity]

 * 无限制的通配符类型(?):如果要使用泛型,但不确定实际的参数类型,就可以使用一个问号代替。
   无限制的通配符类型Set<?>与原生态类型Set的区别:可以将任何元素放入原生态类型Set中,但不能

   将任何元素(除了null)放入到Set<?>中,它是类型安全的。

 * 利用有限通配符类型来提升API的灵活性:? extends E 和 ? super E
   类型通配符上限通过形如ClassName<? extends E>形式定义。
   类型通配符下限为ClassName<? super E>形式。
   extends 表示匹配E及其子类

   不要用通配符类型作为泛型方法的返回类型

 * JDK1.5中一个变化是类 java.lang.Class是泛型化的。这是把泛型扩展到容器类之外的一个很有意思的

   例子。现在,Class有一个类型参数T, 它代表Class对象代表的类型。

   比如说,String.class类型代表 Class<String>

 * 消除非受检的警告:
   用泛型编程时会收到许多编译器警告:unchecked cast warnings、unchecked conversion warnings等。
   如果无法消除警告,同时可以保证引起警告的代码是类型安全的;可以用

   @SuppressWarnings("unchecked")消除警告。应该始终在尽可能小的范围内使用@SuppressWarnings

   注解。永远不要在整个类上使用,这样可能会掩盖了重要的警告。

    一个原生类型很像其对应的参数化类型,但是它的所有实例成员都要被替换掉,而替换物就是这些实例

    成员被擦除掉对应部分参数类型之后剩下的东西。具体地说,在一个实例方法声明中出现的每个参数化

    的类型都要被其对应的原生部分所取代。

    原生类型List 和参数化类型List<Object>是不一样的。如果使用了原生类型,编译器不会知道在list 允许

    接受的元素类型上是否有任何限制,它会允许你添加任何类型的元素到list 中。
    List<?>是一种特殊的参数化类型,被称为通配符类型(wildcard type)。
    像原生类型List 一样,编译器也不会知道它接受哪种类型的元素,但是因为List<?>是一个参数化类型,
    从语言上来说需要更强的类型检查。为了避免出现ClassCastException 异常,编译器不允许你添加

    除null 以外的任何元素到一个类型为List<?>的list 中。
    原生类型的成员被擦掉,是为了模拟泛型被添加到语言中之前的那些类型的行为。
    如果你将原生类型和参数化类型混合使用,那么便无法获得使用泛型的所有好处,而且有可能产生让你

    困惑的编译错误。另外,原生类型和以Object为类型参数的参数化类型也不相同。

 * java1.5增加了泛型(Generics)

   泛型通用参数命名规则:T表示任意的类型,E表示结合的元素类型,K和V表示映射的键和值类型,

   X表示异常,任何类型的序列可以是T、U、V或者T1、T2、T3。

 * 泛型的作用:在编译时检查类型转换,而不是在运行时发现类型转换错误。

   Java中的泛型只是作用于代码编译阶段,在编译过程中,对于正确检验泛型结果后,会将泛型的相关信息

   擦除,也就是说,编译后的class文件中是不包含任何泛型信息的。泛型信息不会进入到运行时阶段。

 * 请不要在新代码中使用原生态类型:
   如果使用原生态类型就失去了泛型在安全性和表述性方面的所有优势。

   使用原生态的例外情况:Class类必须使用原生态类型(List.class);instanceof操作符必须使用原生态

   类型(obj instanceof List)。

 * 泛型有子类型化(subtyping)的规则。List<String>是原生态类型List的一个子类型,不是List<Object>

   的子类型。

 * 列表优先于数组:
   数组和泛型相比有两个重要的不同点:
   (1) 数组是协变式的(covariant),如果Sub是Super的子类型,那么Sub[]也是Super[]的子类型。
        相反,泛型是不可变的(invariant),对于任意两个不同的类型Type1和Type2,List<Type1>既不会

        是List<Type2>的子类型,也不会是超类型。

   (2) 数组是具体化的,因此数组会在运行时才知道并检查元素的类型约束。
        泛型是通过擦除(erasure)来实现的,泛型只在编译时强化它们的类型信息,并在运行时丢弃元素

        类型信息,使泛型可以与没有使用泛型的代码随意互用。
   由于上述这些根本的区别,数组和泛型不能很好地混合使用。

   new E[100]              //编译错误:can not create a generics array of T

   解决的办法:创建一个Object数组并将它转换成泛型数组类型。 (E[]) new Object[capacity]

 * 无限制的通配符类型(?):如果要使用泛型,但不确定实际的参数类型,就可以使用一个问号代替。
   无限制的通配符类型Set<?>与原生态类型Set的区别:可以将任何元素放入原生态类型Set中,但不能

   将任何元素(除了null)放入到Set<?>中,它是类型安全的。

 * 利用有限通配符类型来提升API的灵活性:? extends E 和 ? super E
   类型通配符上限通过形如ClassName<? extends E>形式定义。
   类型通配符下限为ClassName<? super E>形式。
   extends 表示匹配E及其子类

   不要用通配符类型作为泛型方法的返回类型

 * JDK1.5中一个变化是类 java.lang.Class是泛型化的。这是把泛型扩展到容器类之外的一个很有意思的

   例子。现在,Class有一个类型参数T, 它代表Class对象代表的类型。

   比如说,String.class类型代表 Class<String>

 * 消除非受检的警告:
   用泛型编程时会收到许多编译器警告:unchecked cast warnings、unchecked conversion warnings等。
   如果无法消除警告,同时可以保证引起警告的代码是类型安全的;可以用

   @SuppressWarnings("unchecked")消除警告。应该始终在尽可能小的范围内使用@SuppressWarnings

   注解。永远不要在整个类上使用,这样可能会掩盖了重要的警告。

    一个原生类型很像其对应的参数化类型,但是它的所有实例成员都要被替换掉,而替换物就是这些实例

    成员被擦除掉对应部分参数类型之后剩下的东西。具体地说,在一个实例方法声明中出现的每个参数化

    的类型都要被其对应的原生部分所取代。

    原生类型List 和参数化类型List<Object>是不一样的。如果使用了原生类型,编译器不会知道在list 允许

    接受的元素类型上是否有任何限制,它会允许你添加任何类型的元素到list 中。
    List<?>是一种特殊的参数化类型,被称为通配符类型(wildcard type)。
    像原生类型List 一样,编译器也不会知道它接受哪种类型的元素,但是因为List<?>是一个参数化类型,
    从语言上来说需要更强的类型检查。为了避免出现ClassCastException 异常,编译器不允许你添加

    除null 以外的任何元素到一个类型为List<?>的list 中。
    原生类型的成员被擦掉,是为了模拟泛型被添加到语言中之前的那些类型的行为。
    如果你将原生类型和参数化类型混合使用,那么便无法获得使用泛型的所有好处,而且有可能产生让你

    困惑的编译错误。另外,原生类型和以Object为类型参数的参数化类型也不相同。

猜你喜欢

转载自jaesonchen.iteye.com/blog/2286822