3

Is it possible for a generic method in Java to call a specific other method based on the type of the input?

I tried the following example which hopefully represents the problem.

class X {
  public void pr(Object s) {
      System.out.println("Object");
  }

  public void pr(String s) {
      System.out.println("String");
  }

  public <T> void go(T x) {
      this.pr(x);
  }

  public static void main(String[] args) {
    String v = "hello";
    X x = new X();

    // Both print "Object", while I want "String"
    x.go(v);
    x.<String>go(v);
  }
}

I would want go() to call pr(String). But whatever I do, pr(Object) is called.

The example above is for demonstration only, real application of this is much more complex.

Thanks in advance.

Marcin Zukowski
  • 4,281
  • 1
  • 19
  • 28
  • google for "type erasure". There are dozens of questions here about it. The compiler compiles the method to `public void go(Object x)`. The declared type of `x` is thus `Object`. – JB Nizet Aug 26 '14 at 18:00

4 Answers4

5

Unfortunately what you described is not possible because of type erasure, so at runtime T is treated as Object so pr(Object) is chosen here.

One of possible solutions would be manually testing type of passed data and invoking correct method with little help of casting like

public <T> void go(T x) {
    if (x instanceof String)
        pr((String)x);
    else
        pr(x);
}

But if you can edit code of classes you will pass to go method then preferable solution would be adding/implementing pr method in each of them so you could invoke them using polymorphism:

interface CanPR{
    void pr();
}

class Foo implements CanPR{
    @Override
    public void pr() {
        System.out.println("pr from Foo");
    }
}

class Bar implements CanPR{
    @Override
    public void pr() {
        System.out.println("pr from Bar");
    }
}


class X {

    public <T extends CanPR> void go(T x) {
        x.pr();
    }

    public static void main(String[] args) {
        CanPR a = new Foo();
        CanPR b = new Bar();

        X x = new X();

        x.go(a); //prints: pr from Foo
        x.go(b); //prints: pr from Bar
    }
}

Actually in this context you don't even need generic type in go so you can rewrite it to

    public void go(CanPR x) {
        x.pr();
    }
Pshemo
  • 122,468
  • 25
  • 185
  • 269
  • Thanks for the answer. This explains, and I learned about type erasure. Unfortunately, "instanceof" pattern doesn't work for me, as there are too many classes this thing can work on. I will think of some other solution. – Marcin Zukowski Aug 26 '14 at 20:07
  • @MarcinZukowski Unfortunately this is Java and in its early versions backward compatibility was very important so compiled cone need to be able to be run on older versions of JVM which didn't know about generic types `` so type erasure was necessary then, and now it is part of language which no one wants to remove :/ – Pshemo Aug 26 '14 at 20:37
3

Try this:

class X {
      public void pr(Object s) {
          System.out.println("Object");
      }

      public void pr(String s) {
          System.out.println("String");
      }

      public <T> void go(T x) {
          if(x instanceof String){
              this.pr((String)x);
          } else {
              this.pr(x);
          }
      }

      public static void main(String[] args) {
        String v = "hello";
        X x = new X();

        // Both print "Object", while I want "String"
        x.go(v);
        x.go(new Object());
//      x.<String>go(v);
      }
    }

The problem was String is an Object too, so you need to check if it is something else, and that can be done with instanceof.

Bruno Franco
  • 2,028
  • 11
  • 20
  • 1
    Also, we need to check for `x != null` in in `go` method, otherwise it will throw a `MethodAmbiguityException`. – RP- Aug 26 '14 at 18:10
  • Correct, there isn't treatments in the code, but sure it is needed – Bruno Franco Aug 26 '14 at 18:12
  • @Rp- How it is possible to throw any exception here? If x == null then pr(Object s) is called with s == null. I haven't even heard about this exception – Kamil Jarosz Aug 26 '14 at 20:05
  • @kamil09875: You are right, there is no such exception, but it can throw something like `java.lang.Error: Unresolved compilation problem: The method is ambiguous?` if the overloaded parameters are not in inheritance hierarchy. My bad, here it wont throw that exception because String is a subclass of Object. The method with parameter Object will be called in case of nulls. – RP- Aug 27 '14 at 00:48
  • @Rp- Only one case where method may be ambiguous is when you *explicitly* call method(null); when you are calling method method(var) where var is a variable with known type, there's no possibility to ambiguity. I try to understand your thinking, can you give me a little example where it may happen? :) – Kamil Jarosz Aug 27 '14 at 06:48
  • @kamil0985 Okay, consider this case where you have two overloaded methods one with Integer and other with String like `public void m1(Integer i) {//something} public void m1(String s) {//somehting}`. Now if you call `m1(null)` it will throw the exception. This is because Integer and String are not in hierarchy and JRE can not decide which method to call. – RP- Aug 27 '14 at 19:21
3

Java generic is compile time helper, and java compiler stripes away type information for generics.

But you can do

if (x instanceof String) {
  this.pr( (String) x );
}
huocp
  • 3,898
  • 1
  • 17
  • 29
0

Unfortunately what you are described is not possible because due to type erasure at runtime T is treated as Object so pr(Object) is chosen here.

Impossible. I think, its the answer. Just look here

Community
  • 1
  • 1
Meiblorn
  • 2,522
  • 2
  • 18
  • 23