什么是泛型

泛型就是编写模板代码来适应类型,益处就是不用强制转型,通过编译器对类型进行查看

List<String> list = new ArrayList<>();

这就是泛型,汇合限定了元素必须为String类型,否则报错

泛型接口

除了在汇合中应用泛型,有许多接口也用到了泛型,比方 Comparable<T>

能够间接对String类型的数组进行排序,因为String实现了Comparable接口

public final class String implements Serializable, Comparable<String>, CharSequence
String[] s = {"B", "A", "C"};Arrays.sort(s);System.out.println(Arrays.toString(s));

如何实习自定义类的排序呢?实现Comparable接口

public class Person implements Comparable<Person>{    String name;    int age;    public Person(String name, int age) {        this.name = name;        this.age = age;    }    public int getAge() {        return age;    }    public String getName() {        return name;    }    @Override    public int compareTo(Person o) {        return this.getAge()-o.getAge();    }}

测试:

Person[] people = {new Person("lucy", 21), new Person("mary", 19), new Person("Mali", 33)};System.out.println("排序前");for (Person person : people) {    System.out.println(person.getName() + " : " + person.getAge());}Arrays.sort(people);System.out.println("排序后");for (Person person : people) {    System.out.println(person.getName() + " : " + person.getAge());}

通过控制台输入就能够发现Person数组曾经依照年龄进行了升序排序

编写泛型

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;    }}

留神静态方法不能应用<T>,应该与实例类型的泛型辨别开:

public static <K> Pair<K> create(K first, K last) {    return new Pair<K>(first, last);}

擦拭法

虚拟机对泛型是无所不知的,视所有泛型为Object,在须要转型的时候编译器会依据T类型主动为咱们平安的强制转型

这就造成了应用泛型时一些的局限

  1. 不能是根本类型,因为Object无奈持有根本类型
  2. 在获取class时因为擦拭法导致取到的对象是同一个对象
  3. 也无奈判断泛型的类型
  4. 不能实例化泛型

能够借助Class<T>来实例化泛型对象

public Pair(Class<T> clazz) {    first = clazz.newInstance();    last = clazz.newInstance();}

泛型继承

public class IntPair extends Pair<Integer> {    public IntPair(Integer first, Integer last) {        super(first, last);    }}

子类获取父类的泛型类型:

public static void main(String[] args) {    Class<IntPair> clazz = IntPair.class;    Type type = clazz.getGenericSuperclass();    // 取得带有泛型的父类    if (type instanceof ParameterizedType){        // 判断是否为参数化类型,即泛型        ParameterizedType p = (ParameterizedType) type;        Type[] types = p.getActualTypeArguments();    // 父类可能有多个泛型        Type firstType = types[0];    // 取第一个泛型        System.out.println(firstType.getTypeName());    }}

extends

在平时的继承关系中IntegerNumber的子类,然而在泛型中Pair<Integer>不是Pair<Number>的子类

比方咱们定义了一个办法,限定了传入的参数为Pair<Number>类型,如果传入Pair<Integer>就会报错

static int add(Pair<Number> p) {    return p.getFirst().intValue() + p.getLast().intValue();}
public static void main(String[] args) {    Pair<Integer> p = new Pair<>(3,5);    Pair.add(p);}

那如何传入Integer呢?这就须要用到extends通配符来解决了,革新一下那个办法

static int add(Pair<? extends Number> p) {    return p.getFirst().intValue() + p.getLast().intValue();}

这种通配符被称为上界通配符,把泛型类型T的上界线定在了Number

应用extends须知:

add()中是不能获取Integer的援用的,上面代码是无奈通过编译的,要求你强制转型

Integer first = p.getFirst();

因为咱们尽管限定了泛型的上界为Number,然而传入的具体类型到底是Integer还是其余Number的子类是不晓得的,编译器只能确定肯定是Number的子类

也无奈传递Number的子类型给对象的set()

p.setFirst(new Integer(p.getFirst().intValue() + 100));

总结一下:

  1. 容许调用get()获取Number的援用
  2. 不容许调用set(? extends Number)传入任何Number的援用

super

正好和Number相同,传入的是Integer以及它的父类

Pair<? super Integer> p

应用super须知:

public static void main(String[] args) {    Pair<Integer> p = new Pair<>(3,5);    Pair.add(p,5);}static void add(Pair<? super Integer> p,Integer n) {    p.setFirst(n);}

这段代码是能够被失常编译的,因为限定了下界为Integer,无论传入Integer还是它的父类都是能够的

然而无奈应用Integer接管get()的返回值,因为无奈确定具体返回的是Integer还是它的父类,惟一能够接管的是Object

static void add(Pair<? super Integer> p,Integer n) {    Object first = p.getFirst();}

所以比照extendssuper能够发现:

<? extends Number>可读不可写

<? super Integer>可写不可读

补充

申明泛型数组时,不能用new操作符创立数组,需强制转型

Pair<Integer>[] pairs = (Pair<Integer>[]) new Pair[2];