簡體   English   中英

從泛型方法問題調用重載方法

[英]Call of overloaded method from generic method issue

我遇到了有趣的事情(在Java和C#中都一樣)。 Java代碼:

public class TestStuff {

    public static void main(String[] args) {
        Printer p = new PrinterImpl();
        p.genericPrint(new B());    
    }

}

class PrinterImpl implements Printer {

    void print(A a) {
        System.out.println("a");
    }

    void print(B b) {
        System.out.println("b");
    }

    @Override
    public <T extends A> void genericPrint(T b) {
        print(b);
    }

}

interface Printer {
    public <T extends A> void genericPrint(T a);
}

class A {

}

class B extends A{

}

C#代碼:

namespace TestStuff
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            var printer = new Printer();
            printer.GenericPrint(new B());
        }

    }

    public class Printer
    {

        public void Print(A a)
        {
            Console.WriteLine("a");
        }

        public void Print(B b)
        {
            Console.WriteLine("b");
        }

        public void GenericPrint<T>(T a) where T : A
        {
            Print(a);
        }

    }

    public class B : A
    {

    }

    public class A
    {

    }

}

當我寫這樣的東西時,我希望在這兩種情況下都能看到“b”。 但是,正如你所看到的,它是“一個”印刷的東西。

我已經閱讀了C#語言規范,它說在編譯時選擇了重載方法。 它解釋了為什么它以這種方式工作。

但是,我沒有時間在Java語言規范中查看它。

有人可以更詳細地解釋發生了什么以及為什么? 我怎么能實現我想要的?

提前致謝!

關鍵是要理解泛型只能在java的編譯時使用。 它只是編譯器在編譯時使用的語法糖,但在生成類文件時會丟棄。

因此,代碼:

   public <T extends A> void genericPrint(T b) {
      print(b);
   }

編譯成:

   public void genericPrint(A b) {
      print(b);
   }

由於print的參數是A類型,因此重載版本print(A a)是已解析的版本。 我建議對A或者訪問者模式的實例使用多態調用來回調用於您的用例的PrinterImpl。

就像是:

interface Visitor {
   void visit(A a);

   void visit(B b);
}

class PrinterImpl implements Printer, Visitor {

   void print(A a) {
      System.out.println("a");
   }

   void print(B b) {
      System.out.println("b");
   }

   public <T extends A> void genericPrint(T b) {
      b.accept(this);
   }

   public void visit(A a) {
      print(a);
   }

   public void visit(B b) {
      print(b);
   }
}

interface Printer {
   public <T extends A> void genericPrint(T a);
}

class A {
   public void accept(Visitor v) {
      v.visit(this);
   }
}

class B extends A {
   public void accept(Visitor v) {
      v.visit(this);
   }
}

確實在編譯時選擇了重載方法,對於java也是如此(動態方法調度)。 然而,泛型的工作方式略有不同。 您的方法GenericPrinter只能使用A類或其派生類。 它是對該方法的約束。 假設您在GenricPrinter類中調用了A中定義的方法。

public class A
{
    void DoSomethingA()
    {
    }    
}
.
.
.
public void GenericPrint<T>(T a) where T : A
{
    //constraint makes sure this is always valid
    a.DoSomethingA();
    Print(a);
}

因此,此約束將確保僅允許包含上述方法的A或其子類。 雖然傳入A的子類的實例但由於constaint,GenericPrinter會將子類視為A.只需刪除約束部分(T:A),B將按預期打印。

沒有運行時檢查GenericPrint方法中的類型a 你使用where T : A部分強制執行的唯一事情就是你可以調用Print

順便說一下,除了該通用方法:如果希望a被印刷,雖然這是一個實例B ,那么你必須聲明變量作為A obj = new B()

暫無
暫無

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

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