I wonder why this is a valid override:
public abstract class A {
public abstract <X> Supplier<X> getSupplier();
public static class B extends A {
@Override
public Supplier<String> getSupplier() {
return String::new;
}
}
}
Whereas this is not:
public abstract class A {
public abstract <X> Supplier<X> getSuppliers(Collection<String> strings);
public static class B extends A {
@Override
public Supplier<String> getSuppliers(Collection<String> strings) {
return String::new;
}
}
}
According to JLS §8.4.8.1, B.getSupplier
must be a subsignature A.getSupplier
:
An instance method mC declared in or inherited by class C, overrides from C another method mA declared in class A, iff all of the following are true:
- ...
- The signature of mC is a subsignature (§8.4.2) of the signature of mA.
- ...
Subsignatures are defined 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:
So it seems like B.getSupplier
is a subsignature of A.getSupplier
but B.getSuppliers
is not a subsignature of A.getSuppliers
.
I wonder how it can be the case.
If B.getSupplier
is a subsignature of A.getSupplier
because it has the same erasure, then B.getSuppliers
must also have the same erasure as A.getSuppliers
. This should suffice for overriding getSuppliers
to be legal - but it does not.
If B.getSupplier
is a subsignature of A.getSupplier
because it has the same signature, then I wonder what "the same type parameters (if any)" exactly means.
If type parameters are considered, then they should have different type parameters: A.getSupplier
has type parameter X
, B.getSupplier
has none.
If type parameters are not considered then how's getSuppliers
different?
This is more of an academic question about overrides and generics so please don't suggest refactoring code (like moving type parameter X
to the class etc.).
I am looking for a formal, JLS-based answer.
From my point of view B.getSupplier
should not be able override A.getSupplier
as they don't have the same type parameters. This makes the following code (which produces ClassCastException
) legal:
A b = new B();
URL url = b.<URL>getSupplier().get();
According to the compiler output, the method signatures are different in both examples (compile the code with -Xlint:unchecked
option to confirm it):
<X>getSupplier() in A (m2)
1st snippet
getSupplier() in B (m1)
<X>getSuppliers(Collection<String> strings) in A (m2)
2nd snippet
getSuppliers(Collection<String> strings) in B (m1)
According to the JLS specification, 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 of the signature of m2.
The first statement is out of the game - method signatures are different. But what about the second statement and erasure?
B.getSupplier()
(m1) is a subsignature of A.<X>getSupplier()
(m2), because:
- the signature of m1 is the same as the erasure of the signature of m2
<X>getSupplier()
after erasure is equal to getSupplier()
.
B.getSuppliers(...)
(m1) is not a subsignature of A.<X>getSuppliers(...)
(m2), because:
- the signature of m1 is not the same as the erasure of the signature of m2
The signature of m1:
getSuppliers(Collection<String> strings);
Erasure of the signature of m2:
getSuppliers(Collection strings);
Changing m1 argument from Collection<String>
to the raw Collection
eliminates an error, in this case m1 becomes a subsignature of m2.
1st code snippet (valid override): the method signatures in the parent and child classes are different initially. But, after applying the erasure to the parent method the signatures becomes the same.
2nd code snippet (invalid override): the method signatures are different initially and remains different after applying the erasure to the parent method.
The moment you added the parameter it ceased to be an override and became an overload.
Generics have nothing to do with it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With