从generics方法问题调用重载方法

我遇到了有趣的事情(在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  void genericPrint(T b) { print(b); } } interface Printer { public  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 a) where T : A { Print(a); } } public class B : A { } public class A { } } 

当我写这样的东西时,我希望在这两种情况下都能看到“b”。 但是,正如你所看到的,它是“一个”印刷的东西。

我已经阅读了C#语言规范,它说在编译时选择了重载方法。 它解释了为什么它以这种方式工作。

但是,我没有时间用Java语言规范来检查它。

有人可以更详细地解释发生了什么以及为什么? 我怎么能实现我想要的?

提前致谢!

关键是要理解generics仅在java的编译时可用。 它只是编译器在编译时使用的语法糖,但在生成类文件时会丢弃。

因此,代码:

  public  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  void genericPrint(T b) { b.accept(this); } public void visit(A a) { print(a); } public void visit(B b) { print(b); } } interface Printer { public  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也是如此(动态方法调度)。 然而,generics的工作方式略有不同。 您的方法GenericPrinter只能使用A类或其派生类。 它是对该方法的约束。 假设您在GenricPrinter类中调用了A中定义的方法。

 public class A { void DoSomethingA() { } } . . . public void GenericPrint(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

顺便说一句,除了那个通用方法:如果你想要打印,虽然它是B的实例,那么你必须将该变量声明为A obj = new B()