The following code is considered invalid by the compiler:
class Foo {
void foo(String foo) { ... }
}
class Bar extends Foo {
@Override
void foo(Obje
The problem with your code is that you are telling compiler that foo
method in Bar
class is overridden from parent class of Bar, i.e. Foo. Since overridden methods must have same signature
, but in your case, according to syntax it is an overloaded method as you have changed the parameter in foo method of Bar class.
The answer is plain simple, in Java, for method overriding, you must have the exact signature of the super type. However, if you remove the @Override annotation, your method would be overloaded and your code won't break. This is a Java implementation that ensures that you mean the method implementation should override the implementation of the super type.
Method overriding works in the following way.
class Foo{ //Super Class
void foo(String string){
// Your implementation here
}
}
class Bar extends Foo{
@Override
void foo(String string){
super(); //This method is implied when not explicitly stated in the method but the @Override annotation is present.
// Your implementation here
}
// An overloaded method
void foo(Object object){
// Your implementation here
}
}
The methods shown above are both correct and their implementation can vary.
I hope this helps you.
(A rewrite, from a different angle ... my original answer contained an error. :( )
why can't the parameter in the subtype (Bar) be a supertype of the parameter in the supertype (Foo).
I believe that technically it could, and it wouldn't break ancestor contracts following type substition (Liskov Substitution Principle).
From my analysis below, I surmise/guess the rationale for not allowing your scenario:
Compiler Requirements for Method Override - JLS 7
The compiler's required to act accordancing to your experience. 8.4 Method Declarations:
A method in a subclass can override a method in an ancestor class iff:
the return type is type-substitutable for the return type in the ancestor class, i.e. the same type or narrower (8.4.8.3)
Note: subsignature does not mean the overriding method uses subtypes of the overridden method. The overriding method is said to have a subsignature of the overridden method when the overriding method has exactly the same type signature except that generic types and the corresponding raw types are considered equivalent.
Compiler v Runtime Processing for Method Matching & Invocation
There's a performance hit matching method signatures via polymorphic type matching. By restricting override method signatures to exact match of ancestor, the JLS moves much of this processing to compile time. 15.12 Method Invocation Expressions - summarised:
Determine Class or Interface to Search (Compile-Time Determination)
Determine Method Signature (Compile-Time Determination)
Check: Is the Chosen Method Appropriate? (Compile-Time Determination)
Evaluation of Method Invocation (Runtime Determination)
Performance Hit
The breakdown, in terms of text in the JLS:
Step 1: 5% Step 2: 60% Step 3: 5% Step 4: 30%
Not only is Step 2 volumous in text, it's surprisingly complex. It has complex conditions and many expensive test conditions/searches. It's advantageous to maximise the compiler's execution of the more complex and slower processing here. If this was done at runtime, there would be a drag on performance, because it would occur for each method invocation.
Step 4 still has significant processing, but it is as streamlined as possible. Reading through 15.12.4, it contains no processing steps that could be moved to compile time, without forcing the runtime type to exactly match the compile-time type. Not only that, but it does a simple exact match on the method signature, rather than a complex "ancestor type match"
If you can override with superclasses, why not with subclasses as well?
Consider the following:
class Foo {
void foo(String foo) { ... }
}
class Bar extends Foo {
@Override
void foo(Object foo) { ... }
}
class Another extends Bar {
@Override
void foo(Number foo) { ... }
}
Now you have succesfully overriden an method whose original parameter was a String
to accept a Number
. Inadvisable to say the least...
Instead, the intended results may be replicated by using overloading and the following, more explicit, code:
class Foo {
void foo(String foo) { ... }
}
class Bar extends Foo {
@Override
private void foo(String foo) { ... }
void foo(Object foo) { ... }
}
class Another extends Bar {
@Override
private void foo(Object foo) { ... }
void foo(Number foo) { ... }
}
To answer the question in the title of the post What is the reasoning behind not allowing supertypes on Java method overrides?
:
The designers of Java wanted a simple object oriented language and they specifically rejected features of C++ where, in their opinion, the complexity/pitfalls of the feature wasn't worth the benefit. What you describe may have fallen into this category where the designers chose to design/specify out the feature.
Suppose you could do that. Now your superclass looks like this:
class Foo {
void foo(String foo) { ... }
void foo(Number foo) { ... }
}
and your subclass now:
class Bar extends Foo {
@Override
void foo(Object foo) { ... }
}
The language probably could allow such a thing (and just dispatch both Foo.foo(String) and Foo.foo(Number) to Bar.foo(Object)), but apparently the design decision for Java here is that one method only can override exactly one other method.
[Edit]
As dasblinkenlight said in his answer, one can have a foo(Object) without the @Override, but this just overloads the foo functions, and does not override them. When calling, java chooses the most specific method, so foo("Hello World") would always get dispatched to the foo(String) method.