泛型

泛型即广泛的类型,是类型参数化,处理的数据类型是不固定的,作为参数传入。
基本原理:Java编译器将泛型代码转换为普通的非泛型代码——将类型参数T、K擦除,替换为Object;并且插入必要的强制类型转换。Java虚拟机实际执行时,对泛型是不感知的。
泛型主要体现在开发环境和编译阶段,能使代码具有更好的安全性和可读性。(泛型替换Object和强制类型转换)

public class Node<T, K> { //泛型放在类名后
    private T element;
    private K value;

    public Node(T element, K value) {
        this.element = element;
        this.value = value;
    }

    public T getElement() {
        return element;
    }

    public K getValue() {
        return value;
    }
    
    public static <U> int indexOf(U[] arr, U element) { //泛型放在返回值前
        for (int i = 0; i < arr.length; i++) {
            if (element.equals(arr[i])) {
                return i;
            }
        }
        return -1;
    }

    public static void main(String[] args) {
        Node<String, Integer> node = new Node<>("SZG", 99);
        String str = node.getElement();
        int value = node.getValue();
    }
}

见上一代码块,泛型可以用来定义泛型类、泛型方法。同理,还可以用来定义泛型接口,如Comparable接口,及其在Integer中的实现

public interface Comparable<T> {
    public int compareTo(T o);
}
public final class Integer extends Number implements Comparable<Integer> {
    public int compareTo(Integer anotherInteger) {
        return compare(this.value, anotherInteger.value);
    }
    public static int compare(int x, int y) {
        return (x < y) ? -1 : ((x == y) ? 0 : 1);
    }
}

类型参数限定

上界为某个具体类:<T extends Number>,泛型T表示了Number及其子类;在进行类型擦除时,不会转换为Object而是转为Number;
上界为某个接口:<T extends Comparable>,如以下代码块,可以使用compareTo方法。

    public static <V extends Comparable<V>> V max(V[] arr) {
        V max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if (arr[i].compareTo(max) > 0) {
                max = arr[i];
            }
        }
        return max;
    }

以某一泛型为上界,如<T extends E>

泛型将数据结构与算法同数据类型分离,使得同一套数据结构和算法能应用于各种数据类型,而且可以保证类型安全,提高可读性。

通配符参数类型限定

<T extends E>——定义类型参数,声明了一个类型参数T;
<? extends E>——实例化类型参数,实例化了泛型E,但是这个具体的类型是未知的,只知道它是E或E的子类型。
通配符的限制:可读不可写。即可以将元素以E的类型读出;但是不可以以E的子类型方式写入。原因是?并没有约定是E的哪种子类型,不能将E的子类型1写入E的子类型2; 而T extends E限定了元素必须为T,因此读写无阻。
总结:
① 通配符形式可以用类型参数替代;
② 通配符形式可以减少类型参数声明,形式上更简单,可读性也更好;在只读场景下建议使用通配符;
③ 如果参数之间有依赖关系,或者返回值依赖类型参数,或者需要写操作,只能用类型参数;
④ 通配符和类型参数往往配合使用:定义必要的类型参数、使用通配符表达依赖;
<? super E>——用于灵活写入。

局限性

① 不能以基本类型实例化类型参数,如果需要必须使用对应的包装类。
② 运行时类型信息不适用于泛型:如Node<String, Integer>.class是编译错误的,类型与泛型无关。

        Node<String, Integer> node = new Node<>("SZG", 99);
        Node<Integer, Integer> nodeInt = new Node<>(1,1);
        System.out.println(Node.class == node.getClass()); // true,类型就是Node 
        System.out.println(Node.class == nodeInt.getClass()); // true
        System.out.println(node instanceof Node); // true,可以是Node<?, ?> 但不允许写为Node<String, Integer>

③ 由于类型擦除的原因,要考虑到Java虚拟机识别到的代码,避免对接口实现两次,或者方法重复
④ 不能以类型参数实例化对象:仍然是类型擦除的原因,虚拟机真正识别到的其实是父类或者Object,而不是直观理解的某一特定的类型T。但是可以使用反射(newInstance方法)。
⑤ 泛型类中声明的类型参数,不允许在静态方法或代码块中使用——等同于实例变量
不支持创建泛型数组;如果需要存放泛型对象,可以使用原始类型数组泛型容器。泛型容器因为类型擦除的原因,内部其实使用的是Object数组,要转换泛型容器为对应类型的数组,需要使用反射。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值