hymn

忽有故人心头过,回首山河已是秋。

  menu
132 文章
0 浏览
0 当前访客
ღゝ◡╹)ノ❤️

泛型

泛型擦除

编译器看到的:

public class Pair<T> {
    private T first;
    private T last;
    public Pair(T first, T last) {
        this.first = first;
        this.last = last;
    }
    public T getFirst() {
        return first;
    }
    public T getLast() {
        return last;
    }
}

虚拟机看到的:

public class Pair {
    private Object first;
    private Object last;
    public Pair(Object first, Object last) {
        this.first = first;
        this.last = last;
    }
    public Object getFirst() {
        return first;
    }
    public Object getLast() {
        return last;
    }
}

因此,Java使用擦拭法实现泛型,导致了:

  • 编译器把类型<T>视为Object
  • 编译器根据<T>实现安全的强制转型。

使用泛型的时候,我们编写的代码也是编译器看到的代码:

Pair<String> p = new Pair<>("Hello", "world");
String first = p.getFirst();
String last = p.getLast();

而虚拟机执行的代码并没有泛型:

Pair p = new Pair("Hello", "world");
String first = (String) p.getFirst();
String last = (String) p.getLast();

所以,Java的泛型是由编译器在编译时实行的,编译器内部永远把所有类型 T视为 Object处理,但是,在需要转型的时候,编译器会根据 T的类型自动为我们实行安全地强制转型。

小结:

Java的泛型是采用擦拭法实现的;

擦拭法决定了泛型 <T>

  • 不能是基本类型,例如:int
  • 不能获取带泛型类型的Class,例如:Pair<String>.class
  • 不能判断带泛型类型的类型,例如:x instanceof Pair<String>
  • 不能实例化T类型,例如:new T()

泛型方法要防止重复定义方法,例如:public boolean equals(T obj)

子类可以获取父类的泛型类型 <T>

super 和 extends

PECS原则

Producer Extends Consumer Super

Collectionscopy()方法为例:

public class Collections {
    public static <T> void copy(List<? super T> dest, List<? extends T> src) {
        for (int i=0; i<src.size(); i++) {
            T t = src.get(i); // src是producer
            dest.add(t); // dest是consumer
        }
    }
}

无限定通配符

因为 <?>通配符既没有 extends,也没有 super,因此:

  • 不允许调用set(T)方法并传入引用(null除外);
  • 不允许调用T get()方法并获取T引用(只能获取Object引用)。

换句话说,既不能读,也不能写,那只能做一些 null判断:

static boolean isNull(Pair<?> p) {
    return p.getFirst() == null || p.getLast() == null;
}

大多数情况下,可以引入泛型参数 <T>消除 <?>通配符:

static <T> boolean isNull(Pair<T> p) {
    return p.getFirst() == null || p.getLast() == null;
}

<?>通配符有一个独特的特点,就是:Pair<?>是所有 Pair<T>的超类:

Pair<Integer> p = new Pair<>(123, 456);
        Pair<?> p2 = p; // 安全地向上转型
        System.out.println(p2.getFirst() + ", " + p2.getLast());

小结

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

  • 方法内部可以调用传入Integer引用的方法,例如:obj.setFirst(Integer n);
  • 方法内部无法调用获取Integer引用的方法(Object除外),例如:Integer n = obj.getFirst();

即使用 super通配符表示只能写不能读。

使用 extendssuper通配符要遵循PECS原则。

无限定通配符 <?>很少使用,可以用 <T>替换,同时它是所有 <T>类型的超类

ComparableComparator 都是消费者。

泛型和反射

部分反射API是泛型,例如:Class<T>Constructor<T>

可以声明带泛型的数组,但不能直接创建带泛型的数组,必须强制转型;

可以通过 Array.newInstance(Class<T>, int)创建 T[]数组,需要强制转型;

同时使用泛型和可变参数时需要特别小心。

参考自:廖雪峰的官方网站


标题:泛型
作者:hymn
地址:https://dxyhymn.com/articles/2020/11/17/1605600971806.html