Search code examples
javagenericsbounded-types

Java Generics : Convert unbounded types to bounded types


I have two functions defined as

<T1 extends A> void func1(Class<T1> C);

<T2 extends B> void func2(Class<T2> C);

Now user will give any type at runtime, based on what he gives we need to call either func1 or func2. I need something similar to

<T> void func(Class<T> C){
    // check if Class C extends A, then call func1 else call func2
    if (T extends A){
        func1(C);
    }
    else{
        func2(C);
    }
}

I am new to Java Generics and I want to know whether this is possible or not? And if possible how should I do it?


Solution

  • It's not, at least, not with generics.

    Generics are almost entirely a figment of the compiler's imagination.

    At compile time, generics info is there, but only as much as your eyeballs can see. If I write:

    class Example<T> {
        void foo(T in) {}
    }
    

    then your eyeballs cannot tell what T is going to be on any given invocation of the foo method, looking just at this code. So the compiler can't tell either, and at compile time you therefore don't know what the T is. You can't write code that goes: "Give me whatever T is, perhaps in the form of a java.lang.Class instance".

    At runtime this information no longer exists - that's called erasure.

    There is some generics available at runtime - basically, any time generics appears in a signature (that is, the type of a field, of any type mentioned in an extends or implements line, any method return type, any method parameter type) - you can still get that at runtime, but all you get is what is literally written in your code.

    So, once you ask the question: Can I somehow mix runtime concepts such as jlClassInstance.isAssignableFrom and instanceof with a generics bound, the answer is a simple, flat: No. Not possible. - and yet that's what you are asking.

    I did mention that, PROVIDED that the method(s) in question have, in their source code, the types explicitly listed, well, now it's part of signatures and you can theoretically query this at runtime. So if you have:

        <T extends Number> void func1(Class<T> c) { ... }
        <T extends List<?>> void func2(Class<T> c) { ... }
    

    then it IS possible to, at runtime, retrieve, after a ton of hoops and tricky code, Number.class and List.class. But, you shouldn't do this, this is generics abuse. What if someone wrote:

        class Foo<Y> {
            <T extends Y> void func3(Class<T> c) { ... }
        }
    

    Because that would compile fine, and your plan of: I will use reflection to check the actual type bound and use that to at runtime dispatch some stuff - won't work here. All you'll ever be able to tell at runtime here is that the name is 'T', its lower bound is 'Y', and 'Y' is a boundless type param of the enclosing type.