[英]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.