java泛型知识总结

本文深入讲解Java泛型的概念、原理及应用,涵盖泛型类、方法、接口的定义与使用,类型参数的上下界,以及通配符的解析。通过实例演示如何提升代码的复用性和类型安全性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

泛型

笔记上的内容大多是参考java编程的逻辑总结的

1 基本概念和原理

泛型,即“参数化类型”。就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。

1.1 泛型类

1.1.1 单类型参数的泛型类
public class Pair<T> {
    T first;
    T second;
    public Pair(T first, T second){
        this.first = first;
        this.second = second;
    }
    public T getFirst() {
        return first;
    }
    public T getSecond() {
        return second;
    }
}

Pair 就是一个泛型类,与普通类的区别体现在:

  1. 类名后面多了一个

  2. first和second的类型都是T

    T 表示类型参数,泛型就是类型参数化,处理的数据类型不是固定的,而是可以作为参数传入。T只能是类类型包括自定义类,对于int这样的简单类型,要使用包装类。

调用:

Pair<Integer> minmax = new Pair<Integer>(1,100);
        Integer min = minmax.getFirst();
        Integer max = minmax.getSecond();

Integer类型就是传递的实际的参数类型,Pair类的代码和它处理的数据类型不是绑定的,具体类型可以变化,上面可以是Integer,也可以是String。

而且泛型类的类型参数也可以有多个。

1.1.2 多类型参数泛型类
public class Pair<U,V> {
    U first;
    V second;
    public Pair(U first, V second){
        this.first = first;
        this.second = second;
    }
    public U getFirst() {
        return first;
    }
    public V getSecond() {
        return second;
    }
}

调用:

   Pair<Integer,String> minmax = new Pair<>(1,"hh");
        Integer min = minmax.getFirst();
       String str=minmax.getSecond();

而且参数可以不止两个,三个也可以。

public class Pair<U,V,K> {
    U first;
    V second;
    K third;
    public Pair(U first, V second,K third){
        this.first = first;
        this.second = second;
        this.third=third;
    }
    public U getFirst() {
        return first;
    }
    public V getSecond() {
        return second;
    }

    public K getThird() {
        return third;
    }
}

1.2 泛型类内部的原理

对于泛型类,java编译器会将泛型代码转换为普通的非泛型代码,将类型参数擦除,替换为Object,插入必要的强制类型转换。JAVA虚拟机实际执行的时候,它是不知道泛型这回事的。

1.3 泛型方法

* 说明:
 *     1)public 与 返回值中间<T>非常重要,可以理解为声明此方法为泛型方法。
 *     2)只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
 *     3)<T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
 *     4)与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。
public static <T> int indexOf(T[] arr, T elm) {
        for (int i = 0; i < arr.length; i++) {
            if (arr[i].equals(elm)) {
                return i;
            }
        }
        return -1;
    }

对于静态的泛型方法,
调用:

indexOf(new Integer[]{1,3,5}, 10)
或者
indexOf(new String[]{"hello","老马","编程"}, "老马")

静态泛型方法

public class StaticGenerator<T> {
    ....
    ....
    /**
     * 如果在类中定义使用泛型的静态方法,需要添加额外的泛型声明(将这个方法定义成泛型方法)
     * 即使静态方法要使用泛型类中已经声明过的泛型也不可以。
     * 如:public static void show(T t){..},此时编译器会提示错误信息:
          "StaticGenerator cannot be refrenced from static context"
     */
    public static <T> void show(T t){

    }
}

泛型方法参考链接

1.4 泛型接口

public interface Comparable<T> {
public int compareTo(T o);
}
public interface Comparator<T> {
int compare(T o1, T o2);
boolean equals(Object obj);
}

比如Integer类的源码:

public final class Integer extends Number implements Comparable<Integer> {
        public int compareTo(Integer anotherInteger) {
            return compare(this.value, anotherInteger.value);
        }
    }

1.5 类型参数的上下界

对于类型参数,我们都是把它当做Object,但是我们可以限定这个参数为给定的上界类型或其子类型,通过extend关键字来实现。也就是说,指定边界后,类型擦除时,就不会转换为Object了,而是会转换为它的边界类型了。

1.5.1 上界为某一个具体的类

对于NumberPair,其中的类型参数上限限定为Number,对于其中的变量就可以使用Number类中的方法。

public class NumberPair<U extends Number, V extends Number>
            extends Pair<U, V> {
        public NumberPair(U first, V second) {
            super(first, second);
        }
    }
1.5.2 上界为某个接口
public static <T extends Comparable<T>> T max(T[] arr){
//
}
1.5.3 上界也可以为其他的类型参数

比如定义一个容器类,这个类似ArrayList

public class DynamicArray<E> {
    private static final int DEFAULT_CAPACITY = 10;
    private int size;
    private Object[] elementData;
    public DynamicArray() {
        this.elementData = new Object[DEFAULT_CAPACITY];
    }
    private void ensureCapacity(int minCapacity) {
        int oldCapacity = elementData.length;
        if(oldCapacity >= minCapacity){
            return;
        }
        int newCapacity = oldCapacity * 2;
        if(newCapacity < minCapacity)
            newCapacity = minCapacity;
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    public void add(E e) {
        ensureCapacity(size + 1);
        elementData[size++] = e;
    }
    public E get(int index) {
        return (E)elementData[index];
    }
    public int size() {
        return size;
    }
    public E set(int index, E element) {
        E oldValue = get(index);
        elementData[index] = element;
        return oldValue;
    }
}

比如给上面的容器类添加一个实例方法addAll,这个方法将参数容器内的所有元素,都加到当前容器中来,

如果这样写的话:

public void addAll(DynamicArray<E> c) {
        for(int i=0; i<c.size; i++){
            add(c.get(i));
        }
    }

调用:

DynamicArray<Number> numbers = new DynamicArray<>();
        DynamicArray<Integer> ints = new DynamicArray<>();
        ints.add(100);
        ints.add(34);
        numbers.addAll(ints);// 编译会报错

这是因为addAll需要的参数类型为DynamicArray,而传递过来的是DynamicArray.

可以通过类型限定来解决这个问题:

public <T extends  E> void addAll(DynamicArray<T> c) {
        for(int i=0; i<c.size; i++){
            add(c.get(i));
        }
    }

E是 DynamicArray的类型参数,T是addAll的类型参数,T的上界限定为E。刚刚那段代码就没问题了。

2解析通配符

2.1 有限定通配符

对于上面的addAll方法可以通过解析统配符来修改定义

 public void addAll(DynamicArray<? extends E> c) {
        for(int i=0; i<c.size; i++){
            add(c.get(i));
        }
    }

问号表示通配符,<? extends E>表示有限定通配符,匹配E或E的某个子类型,E是DynamicArray的类型限定符。

和<? extends E> 的区别

  1. 用于定义类型参数,它声明了一个类型参数T,可以放在泛型类定义中类后面,泛型方法返回值前面

  2. <? extends E> 用于实例化类型参数,只是这个具体类型是未知的。

    比如:DynamicArray<? extends E> c 就是用来实例化变量c。

2.2 无限定通配符

除了有限定通配符,还有一种通配符,比如DynamicArray<? >。

举个例子:在DynamicArray中查找指定元素

public static int indexOf(DynamicArray<?> arr, Object elm){
        for(int i=0; i<arr.size(); i++){
            if(arr.get(i).equals(elm)){
                return i;
            }
        }
        return -1;
 }

上面的方法定义也可以通过类型参数来定义:可以把上面的方法定义改成这个。

public static <T> int indexOf(DynamicArray<T> arr, Object elm)

补充:<?>是<? extends Object>的简写

读写限制

DynamicArray<? extends Number> numbers = new DynamicArray<>();
        Integer a = 200;
        numbers.add(a);//报错
        numbers.add((Number)a);// 报错
        numbers.add((Object)a); //报错

这是由于问号表示类型安全未知,? extends Number表示是Number的子类型,但是不知道具体是什么类型,如果写入就无法保证类型安全,所以干脆禁止。

参考链接

但是是可以允许读取元素的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Master_Yoda

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值