关于java:泛型

10次阅读

共计 3235 个字符,预计需要花费 9 分钟才能阅读完成。

什么是泛型

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

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

正文完
 0