Java Generics incompatible types for the exact same class

Viewed 411

Here is an example of my Java code:

package com.company;

public class Main {

    private static class Something<T>{
        public void fun(Class<T> someClass){
            someClass.cast(null);
        }
    }
    private interface Hello{}

    public static void some(Something<? extends Hello> mySomething, Class<? extends Hello> myClass){
        mySomething.fun(myClass);
    }
}

And I'm getting a weird error at the mySomething.fun(myClass) line:

Required type: Class<? extends com.company.Main.Hello>
Provided: Class<? extends com.company.Main.Hello>

Which are the exact same type...

What am I missing here?

4 Answers

I believe the problem is that the question mark in the "required" and "provided" can be different. Suppose I have two implementations of Hello: Hello1 and Hello2. I could call:

some(new Something<Hello2>(), Hello1.class);

That fulfills the contract of some, but you don't want to be able to call new Something<Hello2>().someClass(Hello1.class).

I believe you need to express the constraint once, by making some generic:

public static <T extends Hello> void some(Something<T> mySomething, Class<T> myClass)

Now the two parameters are appropriately related, so the call to fun is valid.

Class<? extends com.company.Main.Hello> is a Class whose type parameter is Hello or some class that extends Hello.

Therefore two unrelated Class<? extends com.company.Main.Hello> might represent two different sub-classes of Hello.

For example, suppose you passed to your some(Something<? extends Hello> mySomething, Class<? extends Hello> myClass) method a Something<Hello1> and a Class<Hello2>. Both Hello1 and Hello2 are sub-classes of Hello, but you can't pass a Class<Hello2> argument to the fun() method of Something<Hello1> (which requires a Class<Hello1>).

<? extends MyClass> (in the first parameter) is not guaranteed to be the compatible to <? extends MyClass> (in the second parameter).

If the types were the same, for example:

public static <T> void some(Something<T> mySomething, Class<T> myClass) {
    mySomething.fun(myClass);
}

then compilation passes just fine.

You need to change your some() definition to:

public static <T extends Hello> void some(Something<T> mySomething, Class<T> myClass) {
    mySomething.fun(myClass);
}

This is because in your Something class, the type T is used in the fun declaration in a way that the actual type of T must exactly be the same.

But in your some method, ? extends Hello can be different classes. Hello can have multiple child classes.

Related