从泛型方法问题调用重载方法

时间:2011-12-13 04:26:23

标签: c# java generics overloading

我遇到了有趣的事情(在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语言规范来检查它。

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

提前致谢!

3 个答案:

答案 0 :(得分:5)

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

因此,代码:

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

编译成:

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

由于print的参数是A类型,因此重载版本print(A a)是已解析的版本。我建议对A或Visitor pattern的实例使用多态调用来回调用于您的用例的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);
   }
}

答案 1 :(得分:1)

确实在编译时选择了重载方法,对于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.

答案 2 :(得分:0)

没有运行时检查a方法中的GenericPrint类型。您使用where T : A部分强制执行的唯一事项是,您可以致电Print

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