secret said:
No. Let's take a look at javap here:
jcranmer@quetzalcoatl ~/Desktop $ javap Baz
Compiled from "Quux.java"
interface Baz extends Foo,Bar{
}
jcranmer@quetzalcoatl ~/Desktop $ javap Bar
Compiled from "Quux.java"
interface Bar{
public abstract Bar method(Foo);
}
This is what the compiler sees about the Baz and Bar interfaces. It
actually knows about the T method(S) (a newer version of javap, bundled
with current OpenJDK trunk) will confirm that.
What the compiler will ultimately see is the presence of two methods in
the interface method table:
public abstract Bar method(Foo);
public abstract Foo method(Foo);
But they are. If both methods have the same receiver ("this" object) and
the same parameter signature, they must be the same method. A single
Java class cannot have two distinct method(Foo)s, even with different
return types, in it.
Ah, this is where you are mistaken. Let us discuss the VM for a moment.
When the VM chooses a method to invoke, it's based on the entire
descriptor--including the return type. Covariant returns and generics
erasures mean that you have problems at this point.
Case in point: java.lang.String. If you view its source code (or API),
you will notice that it defines only public int compareTo(String). If
you javap java.lang.String, you will see a public int compareTo(Object).
But it never defined a compareTo(Object), did it?
If you javap java.lang.Comparable, you'll see its sole method:
jcranmer@quetzalcoatl ~/Desktop $ javap java.lang.Comparable
Compiled from "Comparable.java"
public interface java.lang.Comparable{
public abstract int compareTo(java.lang.Object);
}
By the requirements of abstractness, we need to have that
compareTo(Object) class, but we never defined it in source. The compiler
has to generate an implementation:
public int compareTo(java.lang.Object);
Code:
Stack=2, Locals=2, Args_size=2
0: aload_0
1: aload_1
2: checkcast #40; //class java/lang/String
5: invokevirtual #112; //Method compareTo
Ljava/lang/String
I
8: ireturn
What javap isn't printing out is the flags--two flags are going to be
set on this method, the ACC_SYNTHETIC flag (it isn't in source), and the
ACC_BRIDGE flag (it's bridging the actual method).
Again, returning to Quux:
jcranmer@quetzalcoatl ~/Desktop $ javap Quux
Compiled from "Quux.java"
public class Quux extends java.lang.Object implements Baz{
public Quux();
public Quux method(Quux);
public static void main(java.lang.String[]);
public Foo method(Foo);
public Bar method(Foo);
}
You see the two methods--the erasure of the interface methods--that were
never defined in the source.
Let's now emulate a hypothetical compiler as it tries to resolve the method.
b1 is of type Baz. Our method lookup returns two candidates with the
correct number of args, the two methods from the interface. Now we hit
§15.12.2.5 of the JLS:
One fixed-arity member method named m is more specific than another
member method of the same name and arity if all of the following
conditions hold:
* The declared types of the parameters of the first member method
are T1, . . . , Tn.
* The declared types of the parameters of the other method are U1,
.. . . , Un.
* If the second method is generic then let R1 ... Rp p >= 1, be its
formal type parameters, let Bl be the declared bound of Rl, 1 <= l <= p,
let A1 ... Ap be the actual type arguments inferred (§15.12.2.7) for
this invocation under the initial constraints Ti << Ui, 1 <= i <= n and
let Si = Ui[R1 = A1, ..., Rp = Ap] 1 <= i <= n; otherwise let Si = Ui 1
<= i <= n.
* For all j from 1 to n, Tj <: Sj.
* If the second method is a generic method as described above then
Al <: Bl[R1 = A1, ..., Rp = Ap], 1 <= l <= p.
T1 = T
U1 = S
S1 = U1 = S
Is T <: S (is T a subtype of S)? We turn to §4.10.2. A type variable is
a subtype of its bound (and therefore only a subtype of its bound and
its bounds' supertypes). So neither method is more specific than the other.
So we have two maximally specific methods:
# If all the maximally specific methods have override-equivalent
(§8.4.2) signatures, then:
* If exactly one of the maximally specific methods is not declared
abstract, it is the most specific method.
* Otherwise, if all the maximally specific methods are declared
abstract, and the signatures of all of the maximally specific methods
have the same erasure (§4.6), then the most specific method is chosen
arbitrarily among the subset of the maximally specific methods that have
the most specific return type. However, the most specific method is
considered to throw a checked exception if and only if that exception or
its erasure is declared in the throws clauses of each of the maximally
specific methods.
# Otherwise, we say that the method invocation is ambiguous, and a
compile-time error occurs.
Are they override-equivalent? Again, another definition.
Two method signatures m1 and m2 are override-equivalent iff either m1 is
a subsignature of m2 or m2 is a subsignature of m1.
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.
So either the two methods have to be the same signature, or one of the
methods is the erasure of the other. The signatures are
different--remember, we have different parameters T and S--and the
erasure of the first method (method(Foo)) is not the same as the second
(method(S)); the erasure of the second (method(Foo)) is not the same as
the first (method(T)). So the two methods are not override-equivalent.
We therefore hit the ambiguous method invocation clause of the JLS.
You may argue that the JLS is wrong, but the ambiguous method invocation
error conforms to the JLS.
Erasure has nothing to do with the question of whether a single class
can have two distinct method(Foo)s.
The class has two distinct method(Foo)s because of erasure.