我有以下示例:
class Ideone
{
public static void main (String[] args) throws java.lang.Exception
{
A<ConcreteErrorHandler> a = new A<ConcreteErrorHandler>();
a.m(); //Exception here!
}
public static class AbstractErrorHandler {
public static void handle(){
throw new UnsupportedOperationException("Not implemented");
}
}
public static class ConcreteErrorHandler extends AbstractErrorHandler{
public static void handle(){
System.out.println("Concrete handler");
}
}
public static class A<T extends AbstractErrorHandler>{
public void m(){
T.handle();
}
}
}
为什么调用基类的方法,而不是派生的方法? handle()
方法的签名完全相同。我知道静态方法不会继承,但是在我的情况下不应该抛出编译时错误吗?
有人可以解释这种行为吗?
答案 0 :(得分:10)
原因是编译器不知道 AbstractErrorHandler
的哪个确切子类型将在运行时替换T
。这就是为什么它只是将方法调用T.handle()
绑定到AbstractErrorHandler.handle()
方法。
这里的问题是你将继承与Java中的类的static
特性混合在一起。
为了使其正常工作,您必须删除static
方法的.handle()
修饰符,并在T
中保留A
的实例}类。此T
实例(在运行时)将是AbstractErrorHandler
的某个特定子类,然后将执行实际 .handle()
方法。
例如:
class Ideone {
public static void main(String[] args) throws java.lang.Exception {
A<ConcreteErrorHandler> a = new A<ConcreteErrorHandler>(new ConcreteErrorHandler());
a.m();
}
public static class AbstractErrorHandler {
public void handle() {
throw new UnsupportedOperationException("Not implemented");
}
}
public static class ConcreteErrorHandler extends AbstractErrorHandler {
public void handle() {
System.out.println("Concrete handler");
}
}
public static class A<T extends AbstractErrorHandler> {
T instance;
A(T instance) {
this.instance = instance;
}
public void m() {
instance.handle();
}
}
}
答案 1 :(得分:6)
4.4. Type Variables告诉我们:
带有绑定
X
的类型变量T & I1 & ... & In
的成员是在声明类型变量的位置出现的交集类型T & I1 & ... & In
的成员
因此T extends AbstractErrorHandler
的成员是AbstractErrorHandler
的成员。 T.handle();
是指AbstractErrorHandler.handle();
。
答案 2 :(得分:4)
有界类型参数的擦除是绑定(在绑定交集的情况下,绑定中的第一个类型)。因此,在您的情况下,T extends AbstractErrorHandler
将被删除为AbstractErrorHandler
,您的方法将被有效替换为:
public void m() { AbstractErrorHandler.handle(); }
参见例如JLS 4.6
类型变量(第4.4节)的擦除是其最左边界的擦除。
答案 3 :(得分:3)
因为基本上你的方法m
将编译成到
public void m(){
AbstractErrorHandler.handle();
}
答案 4 :(得分:2)
我相信这是因为static
是类作用域的,并且您告诉编译器使用T extends AbstractErrorHandler
隐式使用AbstractErrorHandler。
由于类型擦除在运行时发生,因此运行时将采用最高级别级别。
m
的实现仅使用T
这是一个AbstractErrorHandler,尽管事实上你声明它是main方法中的具体类型,这不在{{1的范围内方法。
答案 5 :(得分:2)
Java编译器会删除通用代码中的所有类型参数,您无法验证在运行时使用泛型类型的参数化类型。因此使用上限类型AbstractErrorHandler
。
有关详细信息,请参阅:https://docs.oracle.com/javase/tutorial/java/generics/restrictions.html
答案 6 :(得分:2)
原因是因为您正在使用隐藏不覆盖的泛型和java静态方法。在编译时,唯一知道的信息是AbstractErrorHandler
类(泛型在java中编译时工作,没有带有泛型信息的字节码),并且调用的方法是类之一。
如果将方法句柄形式static更改为&#34; instance&#34;所谓的实施是&#34;对&#34;一个(因为该方法被覆盖而不是隐藏),如下例所示。
class Ideone
{
public static void main (String[] args) throws java.lang.Exception
{
A<AbstractErrorHandler> a = new A<AbstractErrorHandler>();
a.m(new ConcreteErrorHandler()); //Exception here!
}
public static class AbstractErrorHandler {
public void handle(){
throw new UnsupportedOperationException("Not implemented");
}
}
public static class ConcreteErrorHandler extends AbstractErrorHandler{
public void handle(){
System.out.println("Concrete handler");
}
}
public static class A<T extends AbstractErrorHandler>{
public void m(T t){
t.handle();
}
}
}