泛型

什么是泛型

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

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];
posted @   Asher_z  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示