Java泛型方法重写问题
java 中的泛型是采用类型擦除的方式来实现,也即编译后所有原始类型的泛型类都共享同一份目标代码,例如这里的 A <T> 编译器编译为 A,那么编译器对于引用类中泛型的方法,也即泛型方法进行类型擦除操作时是如何实现的呢?答案是采用最左边类型(当前 T 的初始具体父类型)来代替。如下面代码经过编译器后生成:
编译前的源代码
class A<T> {
T get(final T t) {
return t;
}
}
编译后的 class
A extends java.lang.Object{
A();
java.lang.Object get(java.lang.Object);
}
从上面的代码中可以看出,T 被 Object 代替,也即 T 的当前父类为 Object。如果 A <T extends Comparable>, 那么编译后就会用 Comparable 代替 Object。那进行下面代码分析,B 继承了泛型类 A <T> 时重写泛型类中的泛型方法,此时重写是用 Object 来作为返回值和参数,编译器产生了错误,看下面代码。
class A<T> {
T get(final T t) {
return t;
}
}
/**
* 通过一个类继承了一个泛型类,重写方法时的问题.<br>
* @since JDK 1.5.0
*/
class B extends A<String> {
/**
*@ERROR: Compile-error
* 条件:
* 1、这两个方法(B.get和A.get)具有相同的名称,也即get
* 2、A<String>.get(String)方法B是可以访问得到
* 3、B.get(Object)和A<String>.get(String)的签名signature不是父子关系
* 4、这两个方法具有相同的擦出记号?没明白什么意思....
* @param obj
* @return
*/
Object get(final Object obj) {
return obj;
}
}
错误消息为名称冲突:类型 B 的方法 get(Object)与类型 A 的 get(T)具有相同的擦除记号,但是未将它覆盖。乍一看完全不明白这什么意思,因为从 A.class 来看,擦除泛型后代码中包含的是 Object get(Object), 此时在 B 中定义个相同方法,并应该是符合 java 中的重写语义的吗?从 jls 中翻阅可以得吃 override 条件是满足的,那么编译器为什么不能通过编译?
暂时将问题放一边,将代码进行稍加修改后,编译器编译成功,测试通过,为什么它可以呢?javap B 或者 javap -verbose B 输出指令集进行查看编译结果。
class B extends A<String> {
/**
*@ERROR: Compile-error
* 条件:
* 1、这两个方法(B.get和A.get)具有相同的名称,也即get
* 2、A<String>.get(String)方法B是可以访问得到
* 3、B.get(Object)和A<String>.get(String)的签名signature不是父子关系
* 4、这两个方法具有相同的擦出记号?没明白什么意思....
* @param obj
* @return
*/
String get(final String str) {
return str;
}
}
B extends A{
B();
java.lang.String get(java.lang.String);
java.lang.Object get(java.lang.Object);
}
//具体get方法
java.lang.Object get(java.lang.Object);
Code:
Stack=2, Locals=2, Args_size=2
0: aload_0
1: aload_1
2: checkcast #19; //class java/lang/String
5: invokevirtual #21; //Method get:(Ljava/lang/String;)Ljava/lang/String;
8: areturn
LineNumberTable:
line 1: 0
}
在 B 中只有 String get(String),那么 Object get(Object)从何而来。这是编译器自动生成的一段代码,那么它的目的是什么?从它的内部指定可以看出,调用 String get(String)来实现功能,那么它为什么要调用 String get(String)呢?这又得从 java 泛型擦除说起,B 继承了 A <String>, 那么从编程角度来讲,A<String> 中的泛型方法 get 应该就是 String get(String),此时 B 中的 String get(String)就是重写 A 中的 String get(String),A<String> 是如何来实现的呢?就是通过产生一个桥接方法 Object get(Object)来实现,从这里看出 java 泛型中利用的桥接模式。
总结
写到这里,我想大家应该清楚在 B 中写入一个 Object get(Object)方法是为什么会有 complie-error 了,就是因为编译器产生一个 Object get(Object),此时源代码中又有一个 Object get(Object),一个 class 中同一个方法,具有相同签名和名称的方法重复定义是不符合 java 语法规定,所以有 complie-error。
class A<T> {
T get(final T t) {
return t;
}
}
interface C<E> {
E get(final E e);
}
class D extends A<String> implements C<Integer> {
/**
* 名称冲突:类型 C<E> 的方法 get(E)与类型 A<T> 的 get(T)具有相同的擦除记号,但是未将它覆盖
* {@inheritDoc}
* @see s8_4.C#get(java.lang.Object) 这里是C接口中的实现
*/
public Integer get(final Integer t) {
return null;
}
/**
* 名称冲突:类型 C<E> 的方法 get(E)与类型 A<T> 的 get(T)具有相同的擦除记号,但是未将它覆盖
* {@inheritDoc}
* @see s8_4.A#get(java.lang.Object) 这里是A类的get重写
*/
public String get(final String str) {
return null;
}
}
这个错误原因也是一样。
原文地址:https://blog.csdn.net/qq_26971305/article/details/53938993
JVM 为什么要使用桥方法 https://www.cnblogs.com/wuqinglong/p/9456193.html