java-泛型

一、编写泛型

泛型(Generic)一般用在集合类中 编写泛型时,需要定义泛型类型

public class Pair<T> { … }

静态方法不能引用泛型类型,必须定义其他类型来实现“泛型”

public static <K> Pair<K> create(K first, K last) { … }

泛型可以同时定义多种类型<T, K>

public class Pair<T,K> { … }

二、擦拭法

擦拭法

  • Java的泛型(Generic)是采用擦拭法(Type Erasure)实现的。

  • 编译把视为Object

  • 编译器根据实现安全的强制转型

我们写的是

public static void main(String[] args) {
	Pair<String> pair = new Pair<>("xiao","ming");
	String first = pair.getFirst();
}

实际上编译器处理的是

public static void main(String[] args) {
	Pair pair = new Pair("xiao","ming");
	String first = (String)pair.getFirst();
}

擦拭法的局限

  • 不能是基本类型,例如int

  • Object字段无法持有基本类型

  • 无法取得带泛型的Class(返回的永远的同一个Class) 如:Pair<String>.class

  • 无法判断带泛型类型的类型,例如:x instanceof Pair<String>

  • 不能实例化 T 类型 如:new T()

  • 可以继承自泛型类:

    public class IntPair extends Pair<Integer> {}
    
  • 泛型方法要防止重复定义,例如: public boolean equals(T obj)

  • 子类可以获取父类的泛型类型。

继承关系

graph TD; Type-->Class; Class-->ParameterizedType; ParameterizedType-->GenericArrayType; GenericArrayType-->WildcardType

三、extends通配符

使用类似<? extends Number>通配符作为方法参数时

void someMethod(List<? extends Number> list) {
  	Number n = list.get(0);
  	list.add(n); // ERROR
}

允许传入List,List,List...
允许调用方法获取Number类型
不允许调用方法传入Number类型(null除外)

<T extends Number>定义泛型类

定义泛型时可以通过extends限定T必须是Number或Number的子类


四、super通配符

使用类似<? super Integer>通配符作为方法参数时表示:

  • 允许传入List<Integer>List<Number>List<Object>

  • 允许调用方法传入Integer类型 set方法

  • 不允许调用方法获取Integer类型(Object除外)get方法

    void someMethod(List<? super Integer> list) {
    	list.add(123);
    	Integer n = list.get(0); // ERROR
    }
    

使用类似定义泛型类型时表示:

  • 泛型限定为Integer或Integer的超类

extends和super通配符的区别

  • 允许调用方法获取T的引用
  • 允许调用方法传入T的引用

无限定通配符<?>

  • 只能获取Object引用

  • 只能传入null

  • 可以用消除<?>


五、泛型和反射

部分反射API是泛型:

  • Class

    Class clazz = String.class;
    String str = (String) clazz.newInstance();
    Class<String> clazz = String.class;
    String str = clazz.newInstance();
    Class<? super String> sup = clazz.getSuperclass();
    
  • Constructor

    Class<Integer> clazz = Integer.class();
    Constructor<Integer> cons = clazz.getConstructor(int.class);
    Integer i = cons.newInstance(123);
    
  • 可以声明带泛型的数组,但不能直接创建带泛型的数组,必须强制转型

    Pair<String> [] ps = null;									//正确
    Pair<String> [] ps = new Pair<String>[2];  					//错误
    
    @suppressWarnings("unchecked")
    Pair<String>[] ps = (Pair<String>[]) new Pair[2];  			//正确
    
  • 不安全的使用带泛型的数组:

    public static void main(String[] args) {
      	Pair[] arr = new Pair[2];
      	Pair<String>[] ps = (Pair<String>[])arr;
    
      	ps[0] = new Pair<String>("a","b");
      	arr[1] = new Pair<Integer>(1,2);
      	try {
        		//ClassCastException
        		Pair<String> p = ps[1];
    
      	} catch (Exception e) {
        		System.err.println(e.getMessage()); //java.lang.Integer cannot be cast to java.lang.String
      	}
    }
    
  • 安全的使用带泛型的数组:

    @suppressWarnings("")
    Pair<String>[] ps = (Pair<String>[]) new Pair[2]; 
    
  • 带泛型的数组实际上是编译器进行类型的擦除

    Pair[] arr = new Pair[2];
    Pair<String>[] ps = (Pair<String>[]) arr;
    
    System.out.println(ps.getClass()==Pair[].class); //true
    
    String s1 = (String) arr[0].getFirst();
    String s2 = ps[0].getFirst();
    
  • 可以通过Array.newInstance(Class, int)创建T[]数组,需要强制转型

  • 不能直接创建T[]数组,擦拭后代码变为new Object[]

    //compile error:
    public class Abc<T> {
        T[] createArray() {
          	return new T[5];
    	}
    }
    
  • 借助Class

    public class Abc<T>{
        T[] createArray(Class<T> cls) {
            return (T[])Array.newInstance(cls,5);
        }
    }
    
    //使用
    String[] s = new Abc<String>().createArray(String.class);
    
  • 利用可变参数创建T[]数组,@SafeVarargs消除编译警告

    public class ArrayHelper {
        @SafeVarargs				
        static <T> T[] asArray(T... objs){
        		return objs;
    }
        public static void main(String[] args) {
        		String[] ss = asArray("a","B","c");
        }
    }
    
posted @ 2019-09-10 00:10  马小八  阅读(123)  评论(0编辑  收藏  举报