泛型

关于泛型的几点:

  1. 尖括号里的每个元素都代表一种未知类型
  2. 尖括号只能出现在类名之后(作用于类的泛型)或者方法返回值之前(方法泛型)

使用泛型的好处:

  1. 类型安全 避免粗心导致的类转换异常
  2. 提升代码可读性 编码阶段即可知道对象类型
  3. 提升了代码的复用率

泛型类

class Map<K>{    // 修饰成员变量    private K key;    // 修饰参数    public Map(K key){}    // 修饰返回值    public K get(){        // 修饰局部变量        K key1 = key;        return key1;    }}

泛型方法

// <T> 声明的是这个方法的泛型参数 后面的T声明的是方法的返回类型public static <T> T run(T obj){    return obj;}

泛型限定

// 约定T必须是Comparable的子类<T extends Comparable> // 可同时指定多个父接口<T extends Comparable&Serializable> 

通配符

// 只能接受S的自身或子类<? extends S>// 能接收S自身及其超类<? super S>// 不限制类型,只能使用object接收<?>

PESC原则

Producer Extends Consumer Super

上界<? extends T>不能往里存,只能往外取,适合频繁往外面读取内容的场景。

下界<? super T>不影响往里存,但往外取只能放在Object对象里,适合经常往里面插入数据的场景

List<Apple> apples = new ArrayList<>();apples.add(new Apple());List<? extends Fruit> basket = apples;//按上一个例子,这个是可行的for (Fruit fruit : basket){    System.out.println(fruit);}//basket.add(new Apple()); //编译错误//basket.add(new Fruit()); //编译错误
List<Apple> apples = new ArrayList<>();apples.add(new Apple());List<? super Apple> basket = apples;//这里使用了superbasket.add(new Apple());basket.add(new RedApple());//basket.add(new Fruit()); //编译错误Object object = basket.get(0);//正确//Fruit fruit =basket.get(0);//编译错误//Apple apple = basket.get(0);//编译错误//RedApple redApple = basket.get(0);//编译错误

出现这个原则的原因是因为 List<Apple>List<Fruit> 没有任何关系

如Java API中对集合的复制:

public static <T> void copy(List<? super T> dest, List<? extends T> src) {  ...}

泛型擦除

补救:

如果想要在运行时获取泛型的类型 那就必须通过某种手段记录泛型的 Class 对象

类型变化关系

批注 2019-10-30 131946

A、B是类型,f(·)表示类型转换,≤表示继承关系,如A≤B,表示A继承于B

  1. f(·)是协变(covariant)的,如果 A ≤ B, f(A) ≤ f(B)
  2. f(·)是逆变(contravarian)的,如果A ≤ B,有f(B) ≤ f(A)
  3. f(·)是不变(nvariant)的,当上述两种都不成立,即f(A)和f(B)没有关系
  4. f(·)是双变(bivariant)的,如果A ≤ B,有f(B) ≤ f(A) 和 f(A) ≤ f(B)同时成立