Problem outline
I'm generifying the better part of my current project's base and I had an idea that I decided to test regarding overriding an abstract method. Here are my test classes in Java:
public abstract class Base {
public abstract <T extends Base> T test();
}
First implementation:
public class Inheritor extends Base {
#Override
public Inheritor test() {
return null;
}
}
Second implementation:
public class Inheritor2 extends Base {
#Override
public <T extends Base> T test() {
return null;
}
}
Question 1
Why does it compile? I admit I had high hopes it would be legal, since it makes the contract not only ensure it returns something that does extend Base, but is more specialized already (so that I don't need to cast the result to my specialized class somewhere later).
All sounds nice but do I really fulfill the contract that the base class forces me into? My overriden implementation in Inheritor loses certain layer of genericness doesn't it? My implementation of this method in Inheritor doesn't ever return an instance of Inheritor2, possibility of which the abstract method seemed to enforce (as both extend Base).
I would like pointing to some excerpt from documentation. My guess is it has something to do with type erasure, would be nice if someone mentioned it's accuracy in his/her answer.
Question 2
Does this procedure have a formal name other than one I stated in the title?
Question 3
Is this possible in C#? Colleague's scratch test seemed to fail on compilation. Is there then a difference in approach to generic abstract method overriding?
Here are the technicalities.
Concerning overriding:
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:
A is a superclass of C.
C does not inherit mA.
The signature of mC is a subsignature (§8.4.2) of the signature of mA.
One of the following is true:
mA is public.
[...]
In your case, A is Base and C is Inheritor, Base#test() is mA and Inheritor#test() is mC.
mC is a subsignature of mA because
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.
The erasure of mA is
public abstract Base test()
and mC
public Inheritor test()
is a subsignature. What about the return type?
If a method declaration d1 with return type R1 overrides or hides the
declaration of another method d2 with return type R2, then d1 must be
return-type-substitutable (§8.4.5) for d2, or a compile-time error
occurs.
Following the return-type-substitutable, we see
If R1 is a reference type then one of the following is true:
R1 can be converted to a subtype of R2 by unchecked conversion (§5.1.9).
Inheritor is a subtype of T extends Base through unchecked conversion, so we're all good (though you should have gotten a warning from your compiler).
So to answer your questions:
It compiles because of the rules declared in the Java Language Specification.
It's called overriding.
I don't have a full answer for you, but C# doesn't seem to have type erasure, so these rules wouldn't apply.
The dangers of unchecked conversion would allow you to do
class Inheritor extends Base {
#Override
public Inheritor test() {
return new Inheritor();
}
}
and then
Base ref = new Inheritor();
Inheritor2 wrong = ref.<Inheritor2>test();
which would cause a ClassCastException at runtime. Use it at your own risk.
I can tell you why it should work - Liskov substitution principle
The question to ask is, if you replace Base with Inheritor, or Inheritor2, will all the consumers continue to work without negative consequences? If they expect anything that extends Base from test, then swapping Inheritor2 with Inheritor, or vice versa, will be fine from the perspective a consumer. So, the compiler ought to allow it.
You do really fulfill the contract, which says any subtype of Base can be returned. Any subtype can be one subtype, a random subtype, etc.
Like the commenter Elliott, I believe it's just called overriding a method.
Here's a similar implementation in C# but with generics on the class level.
public abstract class Base<Type>
where Type : Base<Type>
{
public abstract Type test();
}
public class Inheritor:Base<Inheritor>
{
public override Inheritor test()
{
return null;
}
}
public class Inheritor2<Type> : Base<Type>
where Type : Base<Type>
{
public override Type test()
{
return default(Type);
}
}
Related
with the given 2 classes:
public class A{
public <T> Cell<T> f(Cell<T> l){
return null;
}
}
public class B extends A{
public <T> Cell<T> f(Cell<Cell<T>> l){ // trying to override
return null;
}
}
(when Cell is some generic class).
i understand that after type erasure, the classes looks like:
public class A{
public Cell f(Cell l){
return null;
}
}
public class B extends A{
public Cell f(Cell l){ // trying to override
return null;
}
}
before i knew the type erasure thing, i think that i did understand why the compiler sees it as an overloading.
but now that i (think that) i know about the type erasure, the two method signatures are the same, and i really think that it makes much more sense that the compiler will see this as an OVERRIDING.
after compiling it, got that java's compiler saw this as a trying to OVERLOAD and not override, and yields an error for overloading to identical-signatured methods.
my question:
why the compiler still sees this as a trying of OVERLOADING and not OVERRIDING, although after type erasure the signatures are just the same?
thanks!
The error "overloading with same signature" seems adequate here. It is not technically overriding because to the compiler generics matter and B's f does not satisfies A's signature as A's f T is not restricted to implement Cell<?> so B's f is not overriding A's f; it makes more sense to call it "overloading" instead because generic-wise those signatures are different despite end up being the same after erasure.
I reckon that the "overloading with same signature" error was added for this particular scenario with generics resulting in conflicting erasures.
I have a generic method with generic Map parameter. I want to override it and use concrete subtype, but without cast I am not able to return my subtype
public interface GarrageSimple {
<T extends Car> T getCar(Map<String, T> c);
}
Implementation is below which gives error as: Incompatible types. Required: T Found: Bmw
public class GarrageSimpleImpl implements GarrageSimple {
#Override
public <T extends Car> T getCar(Map<String, T> c) {
return new Bmw();
}
}
Actually, Bmw is a subclass of Car which means a type of T. When I cast it like * return (T)new Bmw();* it works with a warning that Unchecked cast Bmw to T
I know that it is because I take a risk to return concrete subclass while method signature is generic, but how I can change my method to make it concrete and not break override signature?
If I can, in that case, it should work as expected.
PS: I don't want to make my class generic, I just need to change my method signature to make it concrete during implementation.
Thanks
No, this is not valid. What would happen if someone with a GarrageSimpleImpl reference called it with a different class extending Car? Generics are all about compile time safety and that's why you get this error.
But you can do it like so,
public interface GarrageSimple<T extends Car> {
T getCar(Map<String, T> c);
}
public class GarrageSimpleImpl implements GarrageSimple<Bmw> {
#Override
public Bmw getCar(Map<String, Bmw> c) {
return new Bmw();
}
}
You can't. A generic method (a method which declares its own type parameters) means that your method must work no matter what the type parameters (here T) are. The method cannot choose what T to use. The caller can call your method with T being anything within the bounds of T, and your method must work correctly with whatever T is without choice.
A subclass overriding a method in a superclass means that the subclass method must be a valid substitute for the overridden method, which means that it must work in all situations that the overridden method can be called in. Since the overridden method is generic, and works for any T, your subclass method that overrides it must also work for any T. Your method does not work for any T -- it fails for any T that is not Bmw.
This question goes from my previous post in here.. Before I post my question, I am pasting the contents from oracle docs;
8.4.8.1. Overriding (by Instance Methods)
An instance method m1, declared in class C, overrides another instance method m2, declared in class A iff all of the following are true:
C is a subclass of A.
The signature of m1 is a subsignature (§8.4.2) of the signature of m2.
8.4.2. Method Signature
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.
My understanding of type erasure when overriding is involved is as follows:
if after erasure, the signature of m1 and m2 are same, then then they are considered overridden.
so in my previous post above, I tried to override a parent class method that takes
List<String> by a subclass method that takes List<Integer> assuming after type erasure
what is left is just List<Object>. but that is wrong. so my understanding of the above definition
of method overriding when erasure is involved is totally wrong. can some give a simple
example to explain the above point.
Thanks. btw the above points come from here.
Whether a method overrides another doesn't just deal with the erasures of the methods. The compiler determines whether a method overrides another, and it has access to the generic type parameters involved before type erasure occurs.
Your thoughts about using erasure to determine overrides are not quite correct. Let's add the following JLS Section, 8.4.8.1, to the discussion:
An instance method m1, declared in class C, overrides another instance method m2, declared in class A iff all of the following are true:
C is a subclass of A.
The signature of m1 is a subsignature (§8.4.2) of the signature of m2.
Either:
m2 is public, protected, or declared with default access in the same package as C, or
m1 overrides a method m3 (m3 distinct from m1, m3 distinct from m2), such that m3 overrides m2.
It's required that m1 is a subsignature of m2, but not the other way around.
Example 1:
class A {
public void foo(List<String> list) { }
}
class B extends A {
#Override
public void foo(List list) {}
}
This is legal, because the signature of B's foo method is the same as the erasure of A's foo method.
Example 2:
class A {
public void foo(List list) { }
}
class B extends A {
#Override
public void foo(List list) {}
}
This is legal, because the signatures are the same (even if they are raw).
Example 3:
class A {
public void foo(List list) { }
}
class B extends A {
#Override
public void foo(List<Integer> list) {}
}
This is not legal, because the erasure of the overriding method is not taken into account. That is, List<Integer> is compared with the erasure of List, which is still just List, and they're not the same.
Example 4:
class A {
public void foo(List<String> list) { }
}
class B extends A {
#Override
public void foo(List<Integer> list) {}
}
This is again not legal, because the erasure of the overriding method is not taken into account. That is, List<Integer> is compared with the erasure of List<String> (List), and they're not the same.
You cannot change the generic type parameters of the parameters in the overriding method (e.g. List<String> to List<Integer>. You cannot introduce generics when overriding a method that didn't utilize generics (e.g. (List to List<Integer>). However, you can remove generics when overriding (e.g. List<String> to List).
Because Java is a strictly-typed language, it has to be careful about covariance and how types work together. Erasure is not an excuse to break the type rules.
For example:
class BaseGood<T> {
public void doStuff(T elem) {
// ...
}
}
class DerivedGood<T> extends BaseGood {
public void doStuff(Object elem) {
super.doStuff(elem);
}
}
class BaseBad {
public void doStuff(List<Double> list) {
// ...
}
}
class DerivedBad extends BaseBad {
public void doStuff(List<Integer> list) {
super.doStuff(list);
// ...
}
}
Here, we have two different cases for erasure.
With the BaseGood and DerivedGood classes, the two methods have the same erasure: void doStuff(Object elem) and it can be known that T will always be of type Object and therefore the function is type-safe.
With the BaseBad and DerivedBad classes, the two methods have the same erasure: void doStuff(List list); however, the type system cannot convert from a List<Integer> to a List<Double> safely (or to and from any List for that matter). Allowing this conversion would potentially allow attempts to put Doubles into lists of Integers (or the issue that generics moves detection of to compile-time).
Having the methods override friendly after erasure does not mean that the type system cannot check for incompatibilities pre-erasure.
EDIT
Also, real erasure as applied to class/method definitions does not occur at compile-time, it occurs at runtime. Code that references generic methods just compile as if the function was called without generics, with type-safety enforced by the compiler and by run-time checking.
See: Java generics - type erasure - when and what happens
There is also another case where you pass a type-erased generic class to an override and because the type system is unable to check whether you passed a proper list based on the type of the method, it must allow it.
Reading the Javadoc for the #Override annotation, I came across the following rule:
If a method is annotated with this
annotation type compilers are required to generate an error message
unless at least one of the following conditions hold:
The method does override or implement a method declared in a supertype.
The method has a signature that is override-equivalent to that of any public method
declared in Object.
I'm clear on the first point, but I'm unsure about the second one.
What does it mean by "override-equivalent"? How are public methods of Object special in this respect? And why is this not covered under the first criterion?
Moreover, this is only true of the Java 7+ documentation. The Java 6 doc doesn't say anything about override-equivalence. Why the change?
Update:
After further consulting the JLS (Section 8.4.2), I found the following explanation of override-equivalence:
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.
As far as I can tell, this answers the first question ("What does it mean?") and the third question ("Why doesn't the first condition cover this?").
If I understand correctly (please inform me if I don't!), there is only one case where two methods are override-equivalent and which doesn't fall under the first condition of the original question. This is the case when the erasure of the signature of the subclass method is the same as the signature of the superclass method, but not the other way around.
The second condition of the original question, then, would only come into play when we attempt to add type parameters when attempting to "override" a public method of the Object class. I tried the following simple example to test this, with an unused type parameter:
public class Foo {
#Override
public <T> boolean equals(Object obj) {
return true;
}
}
Of course, this class doesn't compile, because the method doesn't actually override the equals method and thus clashes with it. But I also still receive a compiler error for using the #Override annotation. Am I wrong in assuming that this example meets the second condition for #Override usage? Or is the compiler generating this error despite not being required to?
The reason for this is to allow you to use the #Override annotation in interfaces, which do not inherit from Object but implicitly declare all public methods from Object (see JLS section 9.2 interface members). You are thus allowed to declare an interface like:
interface Bar { #Override int hashCode(); }
However, you would not be allowed to declare the following interface:
interface Quux { #Override Object clone(); }
since the clone() method is not implicitly declared in an interface (it is not public).
This is described in JLS section 9.6.3.4 #Override (the Javadoc for #Override still refers to an old section number)
Your question is basically a design question and JLS explains its:
"The notion of subsignature is designed to express a relationship
between two methods whose signatures are not identical, but in which
one may override the other. Specifically, it allows a method whose
signature does not use generic types to override any generified
version of that method. This is important so that library designers
may freely generify methods independently of clients that define
subclasses or subinterfaces of the library."
Your code is not a valid example of this , see the below code it works:
public class SubSignatureTest extends SignatureTest {
#Override
public List test(Collection p) {
return null;
}
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
}
}
class SignatureTest {
public <T> List<T> test(Collection<T> t) {
return null;
}
}
Whole point is that signature of superclass and subclass should be same after erasure.
EDIT:
When we talk of override equivalence then parent class should have generic method and child class should have non generic method. Here is an example to explain this .Below code will not work because child class have generic method. For a moment lets assume that java allowed that then the call in main method will always fail :
class A{
public int compareTo(Object o){
return 0;
}
}
class B extends A implements Comparable<B>{
public int compareTo(B b){
return 0;
}
public static void main(String[] argv){
System.out.println(new B().compareTo(new Object()));
}
}
In class B method will be like this after compilation:
public int compareTo(Object x){
return compareTo((B)x);
}
Which means this is always error: new B().compareTo(new Object()) .
Therefore java will not allow child class to have generic method if parent class has non generic method. So you can't define override equivalence methods for object class.
Hope that clarifies.
I used the post http://lists.seas.upenn.edu/pipermail/types-list/2006/001091.html for reference, it has lot more details.
Recently came across an interesting feature, which, though, can result in a unexpected output of Eclipse "add unimplemented methods" feature. What is the "googl-able" name of the language concept behind this "occasional implicit implementation"?
I wouldn't expect the code below to compile but it did and is working
interface CallmeIfc {
public void callme();
}
class CallmeCode {
public void callme() {
// implementation
}
}
class CallmeImpl extends CallmeCode implements CallmeIfc {
// empty class body
}
public static void main(String[] args) {
CallmeIfc me = (CallmeIfc) new CallmeImpl();
me.callme(); // calls CallmeCode.callme()
}
In CallmeImpl, the public callme() method is inherited from CallmeCode, so CallmeImpl respects the contract defined in the CallmeIfc.
Then, in your main() method, polymorphism allows you to assign a subclass instance (CallmeImpl) to a superclass or superinterface reference - in this particular case, the "me" reference, of type CallmeIfc (you have a typo here, BTW).
Actually this looks like a compiler bug to me: The Java Language Specification writes:
An instance method m1 declared in a
class C overrides another instance
method, m2, declared in class A iff
all of the following are true:
C is a subclass of A.
The signature of m1 is a subsignature (§8.4.2) of the signature of m2.
Either
m2 is public, protected or declared with default access in the same package as C, or
m1 overrides a method m3, m3 distinct from m1, m3 distinct from m2, such that m3 overrides m2.
In your case, the first condition is not satisfied: The method callme is declared in class CallMeCode, which is not a subtype of CallmeIfc.
Edit: Bobah is right, the Spec distinguishes between implementing and overriding. In fact, it actually mandates the observed behaviour:
Unless the class being declared is
abstract, the declarations of all the
method members of each direct
superinterface must be implemented
either by a declaration in this class
or by an existing method declaration
inherited from the direct superclass,
because a class that is not abstract
is not permitted to have abstract
methods
The Spec does not explain why.
Although CallmeCode class doesn't implement the CallmeIfc interface, it provides the necessary implementation. It is as if class CallmeCode implements the interface. It would have worked also with this code:
interface CallmeIfc {
public void callme();
}
class CallmeCode implements CallmeIfc {
public void callme() {
// implementation
}
}
class CallmeImpl extends CallmeCode implements CallmeIfc {
// empty class body
}
In your case this is fine because class CallmeCode has a method callme. If the method would have been named different it wouldn't compile.