Calling an overloaded method from a common method problem

I came across an interesting one (it works in both Java and C #). Java Code:

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 # code:

 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 { } } 

When I wrote something like this, I expected to see a "b" printed in both cases. But, as you can see, this is the "a" that is printed.

I read the C # language specification and it says that an overloaded method was selected at compile time. This explains why it works that way.

However, I did not have time to check this in the Java language specification.

Can someone please give a more detailed explanation of what is happening and why? And how could I achieve what I wanted?

Thanks in advance!

+4
source share
3 answers

The key is to understand that generics are only available at compile time in java. This is just the syntactic sugar that the compiler uses when compiling, but discards it when creating class files.

So the code:

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

compiled to:

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

Since the print argument is of type A, the overloaded version of print(A a) is legal. I would suggest using polymorphic calls for instances of A or the Visitor Pattern to call back in PrinterImpl for your use case.

Sort of:

 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); } } 
+5
source

The truth is that overloaded methods are selected at compile time, as well as true for java (sending a dynamic method). However, Generics works a little differently. Your GenericPrinter method can only work with types A or its derivatives. Its restriction on this method. Suppose in your GenricPrinter class you called the method defined in 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); } 

Thus, this restriction will guarantee that only A or its subclasses containing the above method will be allowed. Although you are passing an instance of subclass A, but due to constaint, GenericPrinter will treat the subclass as A. Just remove the restriction part (T: A) and B will print as you expect.

+1
source

There is no execution check whose type a is in your GenericPrint method. The only thing you do with the where T : A part where T : A is call Print .

Btw, besides this general method: if you want to print a , although it is an instance of B , then you must declare this variable as A obj = new B() .

0
source

Source: https://habr.com/ru/post/1386039/


All Articles