Reputation: 11150
I have following code and it is not behaving as i am thinking. I have put the comments inline , that what is happening and what is expected.
class C<T> {
void m(T arg) {
}
}
interface I {
void m(Class arg);
}
class D extends C<Class<String>> implements I {
}
// expected : error -- conflicting inherited methods
// actual: error -- abstract method not overridden
abstract class E extends C<Class<String>> implements I {
}
// expected : error -- conflicting inherited methods
// actual: no error, but no bridge method
Can some one please help me understand this behavior.
Upvotes: 3
Views: 158
Reputation: 999
I expect following discussion in jdk8
.
In the your code:
class D extends C<Class<String>> implements I {
}
should implement the interface I
like this:
class D extends C<Class<String>> implements I {
@Override
public void m(Class arg) {
}
}
Let's see why should implement the method in the interface I
.According the JLS8.4.8.1
:
An instance method mC declared in or inherited by class C, overrides from C another
method mI declared in an interface I, iff all of the following are true:
• I is a superinterface of C.
• mI is an abstract or default method.
• The signature of mC is a subsignature (§8.4.2) of the signature of mI.
In the above code,the mC
is void m(Class<String> arg)
in Class D
,the mI
is void m(Class arg)
And see the specification of subsignature
:
And the `JLS8.4.2` explain the subsignature between two method:
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.
According to above specification ,the void m(Class<String> arg
is not subsignature of void m(Class arg)
,but the void m(Class arg)
is subsignature of void m(Class<String> arg
.So the Class D
should implement the interface I
explicitly.
See following code to explain it:
class C {
public void m(Class arg) {
}
}
interface I<T> {
void m(T arg);
}
class D extends C implements I<Class<String>> {
}
it will compile ok.
The second,Why there is not method conflicting,Because of the JLS8.4.8
explain the inheritance of class:
A class C inherits from its direct superclass all concrete methods m (both static and instance) of the superclass for which all of the following are true:
• m is a member of the direct superclass of C.
• m is public, protected, or declared with package access in the same package as C.
• No method declared in C has a signature that is a subsignature (§8.4.2) of the signature of m.
According to above specification,the method void m(T arg)
with Class<String>
parameter of T
be translated to void m(Class<String> arg)
.And the converted method will be erasured to void m(Class arg)
,this method will be existed in class D
,so the Class D
don't inherit the method void m(T arg)
in class C
.
Upvotes: 0
Reputation: 329
The problem can be reduced to this basic fact about Java generics that it is safe to assign an instantiated generic type to raw type but the reverse is not true. When overriding a method, unsafe conversions are not permitted. Therefore Class<String>
cannot override raw Class type.
In this case:
Class a = String.class; //type safe
Class<String> b = a; //not type safe
Upvotes: 0
Reputation: 8078
I try to explain what's going on, step by step:
Class D
extends C<Class<String>>
which introduces the method with signature void m(Class<String> arg)
at compile-time as of the definition of class C
. But due to type erasure the information about the type parameter String
will be eliminated after compilation. Hence the byte code will create a method void m(Class arg)
defined by class C
.
In addition, class D
implements interface I
which requires D
to implement a method m(Class arg)
which at compile-time is different from the method m(Class<String> arg)
due to the type parameter. Hence you have to provide an implementation for former method or declare D
abstract.
If you provide an implementation for the method m(Class arg)
declared by interface I
you in fact override the method defined by class C
, because both will have the same signature after type erasure is applied.
Things change if you make class D
extend C<Collection<String>>
. In this case class C
will introduce a method with signature void m(Collection<String> arg)
at compile-time which changes to void m(Collection args)
as byte code after type erasure happened. But now the method void m(Class arg)
declared by interface I
does have a different signature and hence will not override but overload method m
defined by class C
.
Here a simple Java example:
class C<T> {
void m(T arg) {
System.out.println("Method [m] of class [C] called");
}
}
class D extends C<Class<String>> implements I {
@Override
public void m(Class arg) {
System.out.println("Method [m] of class [D] called");
}
}
class DD extends C<Collection<String>> implements I {
@Override
public void m(Class arg) {
System.out.println("Method [m] of class [DD] called");
}
}
and here a sequence of calls and the output:
public static void main(String[] args) {
new D().m(D.class);
new D().m((Class<String>) null);
new DD().m(DD.class);
new DD().m(new ArrayList<String>());
}
Output
Method [m] of class [D] called
Method [m] of class [D] called
Method [m] of class [DD] called
Method [m] of class [C] called.
Upvotes: 1
Reputation: 8324
D
is not inheriting anything from I
because it has no default method, just a method signature for which you are indeed not providing an implementation. As @RC points out the type of your m
is different enough that it is not considered a valid implementation. To get the error about a conflict, you would actually need to add a conflicting implementation.
In E
you are marking your class as abstract so it's OK to not have the method implemented as required in the interface. As soon as you do in any class that extends E
, you will trigger the error about conflicting implementations because after type erasure the types would be identical. Basically you just moved the problem from D
to whatever is going to extend E
.
Upvotes: 0