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)
inEntityRegistry
clashes withfindType(String)
inRegistry
; 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!
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 asm1
, orthe signature of
m1
is the same as the erasure (§4.6) of the signature ofm2
.Two method signatures
m1
andm2
are override-equivalent iff eitherm1
is a subsignature ofm2
orm2
is a subsignature ofm1
.
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.