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;
    }
} 

这个错误原因也是一样。

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页