Вызов перегруженного метода из общего вопроса - PullRequest
4 голосов
/ 13 декабря 2011

Я столкнулся с интересной вещью (работает одинаково как на 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" в обоих случаях.Но, как вы можете видеть, это напечатанное "a".

Я прочитал спецификацию языка C # и он говорит, что перегруженный метод выбран во время компиляции.Это объясняет, почему так работает.

Однако у меня не было времени проверить это в спецификации языка Java.

Может кто-нибудь дать более подробное объяснение того, что происходит и почему?И как мне достичь того, чего я хотел?

Заранее спасибо!

Ответы [ 3 ]

5 голосов
/ 13 декабря 2011

Ключ к пониманию того, что дженерики доступны только во время компиляции в 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);
   }
}
1 голос
/ 13 декабря 2011

Это правда, что перегруженные методы выбираются во время компиляции, и это верно и для Java (динамическая диспетчеризация методов).Однако Generics работает немного по-другому.Ваш метод 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 будет напечатан, как вы ожидаете.

0 голосов
/ 13 декабря 2011

Нет проверки во время выполнения того, какой тип a используется в вашем методе GenericPrint. Единственное, что вы выполняете с помощью where T : A, это то, что вы можете позвонить Print.

Кстати, кроме этого универсального метода: если вы хотите, чтобы a был напечатан, хотя это экземпляр B, тогда вы должны объявить эту переменную как A obj = new B().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...