Question

I have read this article here and tried to figure out how to work with bound types. What I try to achieve is a parametrized method that handles four different cases:

  • T extends B only
  • T extends B and I (here D)
  • T extends I only
  • everything else

So here is the code:

public class Main {

    public static void main(String... args) {
        B b = new B();
        D d = new D();
        I i = new I() {
        };
        handle("aaasd");
        handle(b);
        handle(d); <---- Problem 1
        handle(i);
    }

    public static class B {

    }

    public static interface I {

    }

    public static class D extends B implements I {

    }


    public static <T> void handle(T objT) {
        System.out.println("T");
    }

    private static <T extends B> void handle(T obj) {
        System.out.println("B");
    }

    public static <T extends B & I> void handle(T objT) { <--- Problem 2
        System.out.println("B+I");
    }

    private static <T extends I> void handle(T obj) {
        System.out.println("I");
    }
}

The compiler complains and says two things:

  1. Ambiguous call

    The method handle(Main.D) is ambiguous for the type Main I guess the problem is caused by the same cause as Problem number 2. The & I clearly bounds the type of T to a subtype of B AND I thus removing ambiguity in my opinion.

  2. Same erasure handle

    Method handle(T) has the same erasure handle(Main.B) as another method in type Main My guess is that this is the real cause for all the problems. Java somehow removes bounding to I during runtime? But when I call the method with type B this doesn't call the annoted method.

Can someone explain how I fix the problem/distinguish between B, B&I and I?

Was it helpful?

Solution

There's a concept known as type erasure that applies to all generics in Java. With generic methods, after compilation, the methods in the byte code appear as their erasure, so

public static <T> void handle(T objT) {
    System.out.println("T");
}

private static <T extends B> void handle(T obj) {
    System.out.println("B");
}

public static <T extends B & I> void handle(T objT) { <--- Problem 2
    System.out.println("B+I");
}

private static <T extends I> void handle(T obj) {
    System.out.println("I");
}

actually become

public static void handle(Object objT) {
    System.out.println("T");
}

private static void handle(B obj) {
    System.out.println("B");
}

public static void handle(B objT) { 
    System.out.println("B+I");
}

private static void handle(I obj) {
    System.out.println("I");
}

The left-most bound of a type variable is what a parameter of that type gets replaced with. As you can see, both your 2nd and 3rd method have the same name and same parameter types, ie. the same signature. This cannot be allowed by the compiler.

However, the syntax of bounds forces you to provide the class type before any interface types so

<T extends I & B>

wouldn't work. It also wouldn't work because your 4th method would again have the same erasure.

Additionally, invoking

handle(d);

is a problem since both the 2nd and 4th method could handle it, none is more specific. This is known as overloading ambiguity.

OTHER TIPS

Java somehow removes bounding to I during runtime?

No, Java removes every type information at runtime (except for reflection purposes) which is called type erasure.

Using bounds the compiler would be able to translate your code to handle(Object), handle(B) and handle(I) but in the T extends B & I case the compiler would get conflicts.

AFAIK, there's no way to fix this without having a common bound, e.g. T extends D instead of T extends B & I where D extends B implements I or to change the method name or add another parameter.

Another way might be to add the logic in the B+I case to either the B or I method and check for the second condition inside, e.g.

private static <T extends B> void handle(T obj) {
    if( obj instanceof I) { 
      System.out.println("B+I");
    }
    else {
      System.out.println("B");
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top