@XingdingCAO
2017-12-23T08:22:00.000000Z
字数 1704
阅读 4547
Java
泛型
题目:https://www.nowcoder.com/questionTerminal/9ff31e783ce84d1eb1acd260bb5321d5
参考:https://stackoverflow.com/questions/1998544/method-has-the-same-erasure-as-another-method-in-type
class MyGenericClass<T,V>{
T obj1=null;
V obj2=null;
void setValue(T obj){}
void setValue(V obj){}
}
setValue(T)与setValue(V)冲突;两个方法有着相同的擦除(类型擦除)
以下译自参考回答:
javac
编译器会拒绝上述的重载。但是如果你自己通过其他方法(编写自己的编译器或者使用字节码引擎库,比如:ASM
)来通过只识别泛型参数类型(尖括号内的参数)的不同而确定参数签名的不同,那么最终可以产生字节码,而且JVM
也可以正确地调用不同地方法。下面是一个例子来说明为什么javac
会拒绝这样的编译通过:
在泛型(generics)引入前,我写了这样的代码:
class CollectionConverter {
List toList(Collection c) {...}
}
我的类被你继承:
class Overrider extends CollectionConverter{
List toList(Collection c) {...}
}
后来,泛型被引入,我决定更新我的库:
class CollectionConverter {
<T> List<T> toList(Collection<T> c) {...}
//这个方法返回值前也加个<T>的话,这个T就代表该方法自己独有的某个类,而不去和类中限定的T产生冲突
}
overload:(重载)具有相同方法名、不同参数签名之间的方法被调用时,自动选择对应参数签名的方法。此外,当前类中及其父类的成员方法都会参与重载(不包括static、final修饰的)
override:(重写)子类重写/覆盖父类(不限于类,还包括接口)中的某一成员方法(不包括static、final修饰的)
但是你还没准备好升级,所以你没有改动你的Overrider
类。为了正确地重载toList
方法,Java语言设计者决定一个原生类型(raw type)是与任何泛型是“重写等效”的。这就意味着,虽然你的子类中的方法参数签名并不再绝对相等于我的父类中的参数签名,但是你的方法依然参与重写了。
class Overrider extends CollectionConverter {
@Override
List toList(Collection c) {...}
@Override
<T> List<T> toList(Collection<T> c) {...}
}
toList
方法。但当然了,编译器需要决定正确的调用。为了消除模糊性,类是不允许有多个可重写的方法(参数签名相同)的。重点是语言规则设计成允许继续使用原生类型,而不是增加有关方法参数类型不同的限制。
Java
语言,也就是说,上述问题不产生编译错误),这种重载/重写完美生效。因为方法调用的决定发生在编译字节码的时期,是在类型擦除发生前,运行时并不需要类型具体化来使重载生效。(比如说,你的代码中调用了上述的setValue
方法,虽然类型在编译时被擦除,但是运行时仍然能根据你的参数类型决定调用正确的方法)