类型擦除&桥接方法

一提到桥接方法,最常见的应该是23种设计模式其中的1种,但是我们此处提到的桥接方法,是由于在JDK5中泛型的诞生而随之产生的。那么既然要提到桥接方法,就不得不先聊一下它所产生的前因——类型擦除。

类型擦除

泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,挡住向集合中插入非法数据。但编译器编译完带有泛型的java程序后,生成的class文件中将不再带有泛型信息,以此使程序运行效率不受到影响,这个过程称之为“类型擦除”。

下面我们以Animal作为接口,Cat作为实现,描述一下类型擦除。如下是泛型未被擦除的样子:

public interface Animal<T> {
    void eat(T t);
}

public class Cat implements Animal<String> {
    @Override
    public void eat(String s) {
        System.out.println("cat eat " + s);
    }
}

由于生成的class文件中将不带有泛型信息,那么我们将泛型擦除掉,以Object来代替

public interface Animal {
    void eat(Object t);
}

public class Cat implements Animal {
    @Override
    public void eat(String s) {
        System.out.println("cat eat " + s);
    }
}

整体处理方式如下图所示:

泛型被擦除掉了,我们大功告成了!!哎?不对啊!细心的同学们会发现,这个接口定义的是void eat(Object t),但是Cat类中Override的却是void eat(String s),这个是错误的呀,方法签名都不对,不能Override的啊。所以,我们发现,虽然泛型被擦除掉了,并且以Object来代替了,但是程序代码却是错误的了。那怎么办呢?OK,别着急,桥接方法它来了。

桥接方法

由于类型被擦除了,为了维持多态性,所以编译器就自动生成了桥接方法。如下图所示:

通过上图中红色框框住的方法我们可以看到,生成了一个void eat(Object s)方法,这样就可以维持Animal和Cat之间的Override关系。并且在这个方法内,我们通过将入参s强转型为String的方式,增加了对入参的限制,并且最终调用的方法依然是void eat(String s)这个方法。这么一看,泛型被擦除了,并且依然可以保证对入参类型的限制,完美!

不过,这时候对于一些严谨的同学们就会有质疑了,你说的这个真实存在吗?能证明给我们看吗?可以的。还记得反射那节课吧,给大家介绍过如何查看类中所有的方法,那么,我们就来查看一下Cat类中所有的方法有哪些吧。如下所示:

我们发现,通过Cat.class.getDeclaredMethods()方式获得Cat中的方法时,出现了一个根本不是我们编写的类——即:eat(Object o),那么该方法就是桥接方法了。

桥接方法的应用

上面介绍了类型擦除和桥接方法,那么会有同学们疑惑了,这东西有啥用呢?其实它的用处还真的不少呢,尤其是在应用框架上面,当需要使用反射方式访问方法时,就需要先过滤掉桥接方法,因为这个方法毕竟不是我们自己编写的嘛。下面我们来找一下桥接方法的身影吧。

MyBatis

桥接方法在Mybatis中的身影如下图所示:

d4137a22f9fe2fcbf46880de0c524733.png

Spring

桥接方法在Spring中的身影如下图所示:

欢迎大家关注“爪哇缪斯”\(^o^)/~ 「干货分享,每周更新」        

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
泛型的产生条件 泛型是为了解决在编译期间无法确定类型而引入的,其产生条件包括: 1. 在代码中需要使用到泛型类型,但是这个泛型类型的具体类型在编译期间是不确定的; 2. 在代码中需要对泛型类型进行操作,比如调用其方法或者获取其属性值。 泛型的概念 泛型是指在编写代码时,不需要指定数据类型,而是在使用时再指定具体的数据类型。这样就可以提高代码的复用性和灵活性。 泛型类 泛型类是指使用泛型定义的类,其中泛型参数可以用在类的成员变量、成员方法、构造方法中。 泛型类派生类 泛型类派生类是指使用泛型定义的类的子类,其中子类可以继续使用父类中定义的泛型类型。 带泛型子类 带泛型子类是指在继承泛型类时,子类也要使用泛型类型。 不带泛型子类 不带泛型子类是指在继承泛型类时,子类不使用泛型类型。 泛型接口 泛型接口是指使用泛型定义的接口,其中泛型参数可以用在接口的方法中。 泛型方法 泛型方法是指使用泛型定义的方法,其中泛型参数可以用在方法的参数列表、返回值、方法体中。 类型通配符 类型通配符是指在定义泛型时使用的一种特殊符号,用于表示不确定的类型。 引出类型通配符 类型通配符可以用于引出泛型类型参数的上限或者下限。 类型通配符的上限 类型通配符的上限是指使用 extends 关键字限制泛型类型参数的范围,表示泛型类型参数必须是某个类型的子类或者实现类。 类型通配符的下限 类型通配符的下限是指使用 super 关键字限制泛型类型参数的范围,表示泛型类型参数必须是某个类型的父类或者超类。 类型擦除 类型擦除是指在编译期间,将泛型类型参数替换为其上限或者 Object 类型的过程。 无限制类型擦除 无限制类型擦除是指在泛型类型参数没有明确指定上限或者下限时,将其擦除为 Object 类型。 有限制类型擦除 有限制类型擦除是指在泛型类型参数有明确指定上限或者下限时,将其擦除为上限或者下限。 擦除泛型方法类型定义的参数 在泛型方法中,如果定义了泛型类型参数,则在编译期间也会进行类型擦除桥接方法 在泛型类或者泛型接口中,如果有泛型方法,则在编译期间会自动生成桥接方法来确保类型安全。 泛型数组 泛型数组是指使用泛型定义的数组,其中数组元素的类型为泛型类型参数。 泛型与反射 泛型与反射的结合可以实现动态创建泛型类型对象、获取泛型类型信息等功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值