一个你读框架源码之前必须要理解的技术点,泛型(二)

21 篇文章 1 订阅

上一篇(泛型(一))介绍了一下各种定义,包括什么是泛型、泛型的优点、泛型擦除、泛型类、泛型接口、泛型方法、泛型通配符以及泛型上下边界,这一篇主要是上一篇的延续和补充

限定类型变量

在上一篇中,我们知道在使用泛型的时候,可以通过extends 限制泛型实参的上边界,除了限制上边界,我们还可以对泛型的类型变量加以约束,看下例子:

class ExampleClass8{
	//计算最小值
    public <T extends Comparable> T min(T paramA, T paramB){
        return paramA.compareTo(paramB)>0? paramB: paramA;
    }
}

为了确保传入方法的两个参数都具有compareTo方法,这里可以限制T 实现了Comparable接口。在使用的时候,如果传入了的泛型实参没有实现Comparable接口,就会报错。

还有一点需要注意的是,extends前面可以放置多个泛型,extends之后如果有多个类型的话,类型之间用&符号隔开,而且如果有类类型只能有一个类类型,第一个必须是类类型,其余的是接口类型

 //第一个必须是类类型,这个会报错
 public <T extends Comparable & Apple> T min(T paramA, T paramB){...}
 //正确
 public <T extends Apple & Comparable> T min(T paramA, T paramB){...}
 //有类类型只能有一个类类型,报错
 public <T extends Apple & Orange &Comparable> T min(T paramA, T paramB){...}
 //正确
 public <T extends Apple& TestInterface1 & Comparable> T min(T paramA, T paramB){...}

另外,这种限定既可以用在泛型方法上也可以用在泛型类上。泛型类上的使用就不举例了。

泛型类型的继承关系

泛型类可以继承或实现(扩展)其它泛型类,比如ArrayList

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable{...}

如果两个类存在继承关系,如果在泛型类中使用这两个类作为泛型实参,那这两个泛型类是不存在继承关系的。
例子如下
在这里插入图片描述

public class ExampleUnitTest {
    @Test
    public void test() {
    	ExampleClass9<Fruit> fruit = new ExampleClass9<Apple>();
    }
}

class ExampleClass9<T> {
    public void eat(ExampleClass9<Fruit> fruit){
    }
}
public class Fruit {
    private String name;

    public Fruit() {
    }

    public Fruit(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
public class Apple extends Fruit {
    public Apple() {
    }

    public Apple(String name) {
        super(name);
    }

    @Override
    public String toString() {
        return "Apple{}";
    }
}

由于ExampleClass9< Fruit>与ExampleClass9< Apple>是没有关系的两个类,如果在一个方法中同时传入这两种参数是行不通的,但是正常不使用泛型类的情况下是可以向上转型的(多态),
为了解决这种问题,引入了泛型通配符的概念,这个可以看一下上一篇。

泛型中的约束与局限性

1.泛型参数不能用基本数据类型
2.运行时类型查询只适用于原生类型
泛型类中类的泛型不论传的时什么类型,getClass获取都是原生类的类型,获取的类名都是一样的,且泛型类不能使用instanceof 判断类的类型
在这里插入图片描述
在这里插入图片描述
打印
在这里插入图片描述
3.在泛型类中,不能在静态域或方法中使用泛型类中的泛型,除非是一个静态的泛型方法

在这里插入图片描述

Java虚拟机是如何实现泛型的

泛型是JDK 5.0的新特性,在此之前是没有泛型的概念的,这时的Java只能通过Object是所有类型的父类和强制类型转换两个特点来实现类型泛化。
由于java.lang.Object是所有Java类的父类,所以Object转型成为任何对象都是有可能的。这个特点,它是有缺陷的,因为只有程序员和运行期的虚拟机才知道Object到底是什么类型的对象,程序编译期间,编译器不能检查Object是否能够强转成功,程序都是人写的,不能保证每次强转的正确性,造成许多类型转换异常(ClassCastException)的风险发生在运行期。

Java引入的泛型为了兼容JDK5.0之前的代码并不是一个真正意义上的泛型,实际上是Java语言的一个语法糖,它只在程序源码中存在,在编译后的字节码文件中,被替换为原来的原生类型(Raw Type,也称为裸类型),并在相应的地方插入了强制转换代码,这个也被成为泛型擦除。因此对于运行期来说ArrayList< Integer>于ArrayList< Double>其实就是同一个类。为了在泛型类中获取传入的参数化类型,虚拟机规范中添加了Signature、LocalVariableTypeTable等新的属性用于解决伴随泛型而来的参数类型的识别问题。Signature的作用是存储一个方法在字节码层面的特征签名,这个属性中保存的参数类型并不是原生类型,而是包括了参数化类型的信息。

泛型擦除,仅仅是对方法的Code属性中的字节码进行擦除,实际上元数据中还保留了泛型信息,这也是我们能通过反射手段获得参数化类型的根本依据。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值