Why does overriding this generic method only work if the base method includes an unused type parameter?

enveeed :

I've encountered a rather weird thing while working on my Java project.

I have this base method in an interface:

public interface Registry<T extends FlakeType<? extends F>, F extends Flake> {

    @Nullable
    public <E extends F> T findType(@Nonnull String name);
}

Obviously the type parameter E is completely useless since it is neither used in the return type nor the arguments. My IDE also notifies me about that.

And I have another interface extending Registry:

public interface EntityRegistry extends Registry<EntityType<? extends Entity>, Entity> {

    @Nullable
    @Override
    <E extends Entity> EntityType<E> findType(@Nonnull String name);

}

For context, Entity extends Flake, EntityType<E extends Entity> extends FlakeType<E>.

As you can see I am attemting to specify the return type of this method from EntityType<? extends Entity> (this type is coming from the T type parameter) to EntityType<E>, while E is a method type parameter <E extends Entity>.

Logically, ? extends Entity and E extends Entity mean the same thing in this case since E has no other restrictions or something like that and because of this acts essentially also as a wildcard for types extending Entity.

This works perfectly fine.


However, ive noticed the notification by my IDE (Intellij) that

Type parameter E is never used.

This made me think that I could remove it since it is obviously useless and does not have any relationship to either the return value or any argument.

The overriding method provides its own E type parameter which should in no way have something to do with a useless type parameter with the same name in the base method. Or has it?

Because once I changed my base method to this:

@Nullable
    public T findType(@Nonnull String name);

overriding it the same way was no longer possible:

findType(String) in EntityRegistry clashes with findType(String) in Registry; both methods have same erasure, yet neither overrides the other.


My questions are now:

  • Why does Java require a useless type parameter in the base method when an overriding method wants to specify the return values generic type? Shouldn't the parameter provided by the overriding method be sufficient?

  • Why does overriding in the second case NOT work? Shouldn't it work because the signature is exactly equal to the first case, except that instead of the explicit wildcard another type parameter is used, but since that does mean the same thing, what is the difference?

I am sure I have made some wrong assumptions about generics and this is why I am confused by this. Could you clear it up for me?

Thanks in advance!

Jorn Vernee :

Type parameters are part of the method signature. You can find this in JLS §8.4.2:

Two methods or constructors, M and N, have the same signature if they have the same name, the same type parameters (if any) (§8.4.4), and, after adapting the formal parameter types of N to the the type parameters of M, the same formal parameter types.

The signature of a method m1 is a subsignature of the signature of a method m2 if either:

  • m2 has the same signature as m1, or

  • the signature of m1 is the same as the erasure (§4.6) of the signature of m2.

Two method signatures m1 and m2 are override-equivalent iff either m1 is a subsignature of m2 or m2 is a subsignature of m1.

Without the type parameter in Registry, the method in EntityRegistry does not have a subsignature of the base interface method, so it is not seen as an override.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=80061&siteId=1