Return bounded type is incompatible with a type that matches the bound

267 Views Asked by At

I’m pretty sure something like this has been asked but I can’t really find the exact wording, so here it goes.

If I have these three classes:

package boundedtypetest;

public class Factory {
    private class ClassA {}

    private class ClassB extends ClassA {}

    public <T extends ClassA> T create() {
        return new ClassB();
    }
}

Why does the Java compiler say that T and ClassB are incompatible in the create method?

3

There are 3 best solutions below

1
On BEST ANSWER

You've created a generic method by declaring T as a type parameter with an upper bound. With generic methods, you must be aware that the caller can decide what T is by passing an explicit type argument to your method.

class ClassC extends ClassA {}

ClassC c = new Factory().<ClassC>create();

There is no guarantee that the type parameter chosen by the caller, explicitly or implicitly, will match the type of what is returned, and you're returning a ClassB. The compiler cannot guarantee type safety here so this is disallowed.

If you don't need the generics, remove the type parameter off the method and declare create to return a ClassB, ClassA, or Object.

If you need the generics, then you must take a parameter of type Class<T> and create an instance with it to satisfy the compiler.

public <T extends ClassA> T create(Class<T> clazz) throws ReflectionRelatedExceptions 
{
    return clazz.getConstructor().newInstance();
}
0
On

The problem is that you'll be able to store a ClassB in another class that extends ClassA

For example you have a ClassC which extends ClassA same as ClassB does, you can store the result of create() in a ClassC object, but the method would be able to return a ClassB instance ? That's why you can't

class Factory {

    void method() {
        ClassC c = create();     //This is correct, regarding the return type of the method
    }                            // But it would allow to store ClassB in ClassC : NOPE

    public <T extends ClassA> T create() {
        return new ClassB();
    }

    private class ClassA { }

    private class ClassB extends ClassA { }

    private class ClassC extends ClassA { }
}
0
On

Its because T could be any sub-class of ClassA. To illustrate, say you have another class ClassC that extends ClassA

class ClassC extends ClassA {
}

Now you can never be sure of which subclass of ClassA will be used to call this method.

class Factory {
    public <T extends ClassA> T create() {
        return new ClassB();
    }
}

Here T could be ClassB, ClassC, etc. Hence it is not possible for you to return one of the possible sub-classes of ClassA

Detailed information can be found in this SO question.