调用类型变量

时间:2015-05-13 12:43:50

标签: java generics static

我有以下示例:

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

IDEONE

为什么调用基类的方法,而不是派生的方法? handle()方法的签名完全相同。我知道静态方法不会继承,但是在我的情况下不应该抛出编译时错误吗?

有人可以解释这种行为吗?

7 个答案:

答案 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();
        }
    }
}