簡體   English   中英

調用類型變量的基類和派生靜態方法

[英]Calling base and derived static methods of a type variable

我有以下示例:

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()方法的簽名完全相同。 我知道靜態方法不會繼承,但是在我的情況下不應該拋出編譯時錯誤嗎?

有人可以解釋這種行為嗎?

原因是編譯器不知道 AbstractErrorHandler哪個確切子類型將在運行時替換T 這就是為什么它只是將方法調用T.handle()綁定到AbstractErrorHandler.handle()方法。

這里的問題是你將繼承與Java中的類的static特性混合在一起。

為了使其工作(正確),您必須擺脫.handle()方法的static修飾符,並在A類中保留T的實例。 這個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();
        }
    }
}

4.4。 類型變量告訴我們:

具有綁定T & I 1 & ... & I n的類型變量X的成員是交叉類型T & I 1 & ... & I n的成員,它們出現在聲明類型變量的位置

因此成員T extends AbstractErrorHandler是成員AbstractErrorHandler T.handle(); 指的是AbstractErrorHandler.handle();

有界類型參數的擦除是綁定(在綁定交集的情況下,綁定中的第一個類型)。 所以在你的情況下, T extends AbstractErrorHandler被擦除為AbstractErrorHandler ,你的方法被有效地替換為:

 public void m() { AbstractErrorHandler.handle(); }

參見例如JLS 4.6

類型變量(第4.4節)的擦除是其最左邊界的擦除。

因為基本上你的方法m編譯

public void m(){
    AbstractErrorHandler.handle();
}

我相信這是因為static是類作用域的,你告訴編譯器使用T extends AbstractErrorHandler隱式使用T extends AbstractErrorHandler

由於類型擦除在運行時發生,因此運行時將采用最高級別級別。

m的實現只使用T這是一個AbstractErrorHandler,盡管事實上你聲明它是main方法中的具體類型,這不在m方法的范圍內。

Java編譯器擦除通用代碼中的所有類型參數,您無法驗證在運行時使用泛型類型的參數化類型。 因此使用上限類型AbstractErrorHandler

有關詳細信息,請參閱: https//docs.oracle.com/javase/tutorial/java/generics/restrictions.html

原因是因為您使用的是隱藏不覆蓋的泛型和java靜態方法 在編譯時,唯一知道的信息是AbstractErrorHandler類(泛型在java中編譯時工作,沒有帶有泛型信息的字節碼),並且調用的方法是類之一。 如果將方法句柄形式static更改為“instance”,則調用的實現是“正確的”(因為該方法被覆蓋而不是隱藏),如下例所示。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM