Say you have an Interface A, and an Interface B. Let's say the Sprite class implements both interfaces.
Say there's another class that has a method foo(A object), and also has a method foo(B object).
Do both get called when I pass an instance of Sprite to the method foo()? If not, which has precedence?
With method overloading (as is in use here), the method to call is resolved at compile time, based on the (declared) type of the variable holding the Sprite.
Since the method call is ambiguous, this will fail to compile until you manually downcast the reference to resolve the ambiguity.
It's ambiguous. You'll need to cast to one of the interfaces.
interface A {}
interface B {}
class Sprite implements A,B {}
class Test{
void foo(A a){}
void foo(B b){}
void test(){
Sprite s = new Sprite();
foo(s); // <-- compile time error (The method foo(A) is ambiguous for the type Test)
}
}
The general answer requires several pages to explain; see the relevant section in the language specification.
In the specific case you present, it just won't compile. But in other cases there are rules by which the compiler may prefer one overload to another and resolve the conflict.
The most important rule is that, in general, if one method is more specific than the others, that method is picked. Method A is more specific than method B if all possible invocations of method A would also compile if calling method B. This usually means that some of the formal parameter(s) of method A are subclassed from the corresponding formal parameter(s) in method B. There are a few cases where this won't apply, such as when auto-boxing or other "method conversion" would be required to make an argument fit in the otherwise "more specific" case. For specifics, see the spec.
Related
I have a class with a type parameter.
class MyObject<IdType> {
#Setter
#Getter
private IdType id;
}
And I thought I can add some method for conveniency so I did.
<T extends MyObject<? super IdType>> void copyIdTo(T object) {
object.setId(getId());
}
< T extends MyObject<? extends IdType>> void copyIdFrom(T object) {
object.copyIdTo(this);
}
And I just realized that I can do this.
void copyIdTo(MyObject<? super IdType> object) {
object.setId(getId());
}
void copyIdFrom(MyObject<? extends IdType> object) {
object.copyIdTo(this);
}
Are those two sets of methods are equivalent? Which way (or style) is prefer?
In your case, the two approaches are effectively equivalent. They both restrict the argument's type to MyObject<...> or a subtype.
Since your example methods return void there's no real benefit from making the method generic. The only important thing for your method is that the argument is a MyObject<...>—beyond that the real type is meaningless. Adding the ability to make the argument's type more specific adds nothing for the method's implementation and does nothing for the caller. In other words, it's irrelevant "fluff".
So for your examples, I would say prefer the non-generic option. It's cleaner and more straightforward.
However, if your methods returned the given argument back to the caller then making the method generic could prove useful; it would allow you to declare the return type as T. This opens up possibilities to the caller such as method chaining or invoking the method "inside" another method call, all based on the specific type passed as an argument. An example of this in the core library would be Objects.requireNonNull(T).
Another good case for making the method generic is mentioned by #Thilo in the comments:
Another case would be if your method takes multiple arguments. Then you can introduce a T to make sure those two arguments have the same type (instead of two distinct types that happen to [fulfill] the constraints individually).
Yes they are equivalent. Both sets of methods declare the same thing - that the method parameter must be of type MyObject<> or a compatible subtype (subclass).
The only reason to declare T in this way is if you need to refer to T elsewhere, such as the return type of the method, or if you have multiple parameters of the same type, or inside the method body.
I would always prefer the shorter, simpler, clearer version with less angle brackets to hurt the eyeballs :)
I'm reading about type inference for generics, and this code was provided as an example that fails to compile.
import java.io.*;
class LastError<T> {
private T lastError;
void setError(T t){
lastError = t;
System.out.println("LastError: setError");
}
}
class StrLastError<S extends CharSequence> extends LastError<String>{
public StrLastError(S s) {
}
void setError(S s){
System.out.println("StrLastError: setError");
}
}
class Test {
public static void main(String []args) {
StrLastError<String> err = new StrLastError<String>("Error");
err.setError("Last error");
}
}
And the explanation given in the book was:
"(It looks like the setError() method in StrLastError is overriding setError() in the LastError class. However, it is not the case. At the time of compilation, the knowledge of type S is not available. Therefore, the compiler records the signatures of these two methods as setError(String) in superclass and setError(S_extends_CharSequence) in subclass—treating them as overloaded methods (not overridden). In this case, when the call to setError() is found, the compiler finds both the overloaded methods matching, resulting in the ambiguous method call error."
I really don't understand why type S can't be inferred at compile time.
String is passed when invoking the constructor of class StrLastError,
and from the API docs, String does implement interface CharSequence,
so doesn't that mean that S for <S extends CharSequence> actually is of type String?
I've read the Java online tutorial on the topic of generics several times. I've checked "Type Inference", and Inheritance, I just don't know how the whole thing works. I really need an explanation on this question.
The points I'm stuck at are:
If the subtype can't decide S, how come the super type can decide T, because the superclass does not have an upper bound? Or does it infer that T is String because the subtype calls the supertype's constructor first?
I understand that if the Constructor is invoked as:
StrLastError<CharSequence> err = newStrLastError<>((CharSequence)"Error");
there will be no ambiguity, since it's plain method overriding then. (Or am I even wrong here?)
However, like I said in the beginning, if String is passed, why can't S be inferred to be String?
You have to remind yourself that the classes are compiled one by one. Java generics are not templates as in other languages. There will only be one compiled class and not one class per type it is used with.
This way you can see that the class StrLastError will need to be compiled in a way such that it can also be used with other classes implementing CharSequence as generic type S.
Thats why the compiler gets you two different methods instead of an overridden one. Now it would be a runtime-job to see that the subclass may have wanted to override the method in the parent just in those cases where the types suggest it. Since this behaviour is hard to understand for the developer and would possibly lead to programming mistakes, it raises an exception.
If you use CharSequence as the generic type parameter of the class StrLastError, you will call the setError method in the parent class, since the type of "Last Error" is String, which is more specific than CharSequence and Java always chooses the most specific method in case it is overloaded. (I hope it is clear the the method was not overridden in this case either)
If the subtype can't decide S, how come the super type can decide T, because the superclass does not have an upper bound? Or does it infer that T is String because the subtype calls the supertype's constructor first?
The super type isn't deciding or inferring what T is; you're explicitly telling it what T is by this declaration:
class StrLastError<S extends CharSequence> extends LastError<String>
T is now bound to String for LastError, which makes every reference to T in the parent class a concrete String.
Your child class now has a bound S extends CharSequence attached to it, but this is independent to the bounds applied to the parent class.
What happens now is that Java will compile your child class, and the result of your child class is that two methods with a signature that matches String will be created. (Key note here: A String is-a CharSequence.)
In your child class, setError(Ljava/lang/CharSequence;)V is generated as the signature for setError. Because of the way generics work, LastError#setError will be treated as if it has a signature of setError(Ljava/lang/String;)V. This is also why when you go to actually override the method, it will place a String type as your parameter instead of anything else.
So, what we arrive at are two methods that have override-equivalent signatures.
void setError(CharSequence s)
void setError(String s)
JLS 8.4.8.4. applies here.
It is possible for a class to inherit multiple methods with
override-equivalent signatures (§8.4.2).
It is a compile-time error if a class C inherits a concrete method
whose signature is a subsignature of another concrete method inherited
by C. This can happen if a superclass is generic, and it has two
methods that were distinct in the generic declaration, but have the
same signature in the particular invocation used.
I understand that if the Constructor is invoked as StrLastError err = new StrLastError<>((CharSequence)"Error"); there will be no ambiguity, since its plain method overriding then.(Or I'm even wrong here)
No, now you're messing with raw types. Interestingly enough it will work, primarily because the signatures for the two methods has become:
void setError(Object s)
void setError(String s)
You want to use generics to avoid a scenario like this; you may want to invoke the super class method at some point, but in this scenario with these bindings, it's very difficult to accomplish.
This is a tricky example, to fix it you need to change the following line:
class StrLastError<S extends CharSequence> extends LastError<S>
The following code is considered invalid by the compiler:
class Foo {
void foo(String foo) { ... }
}
class Bar extends Foo {
#Override
void foo(Object foo) { ... }
}
I think that this is described in the JLS 8.4.8.1: "The signature of m1 is a subsignature (§8.4.2) of the signature of m2." and in 8.4.2: "the bounds of corresponding type variables are the same".
My question is: why can't the parameter in the subtype (Bar) be a supertype of the parameter in the supertype (Foo). In the example Object is a supertype of String. As far as I can see allowing this wouldn't violate the Liskov Substitution Principle.
Is there a scenario in which allowing this would break the code or is it a limitation of the current JLS?
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.
(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).
Types are passed-by-value (including reference types).
The caller can never be forced to deal with different parameter types than what it passes in.
A method body can swap a parameter type, but can't return it back to the caller (no such thing as 'output parameter types').
If your proposal was allowed, and a call was made to the ancestor method signature, a decendent class could override the method with broader types, but it's still impossible to return a broader type than what the caller sets.
The override could never break a client that uses the narrow ancestor method contract.
From my analysis below, I surmise/guess the rationale for not allowing your scenario:
Performance-related: allowing broader types in override would impact runtime performance, a major issue
Functionality-related: it only adds a minor amount of functionality. As it stands, you could add your 'broader method' as an overloaded method without overriding. Then you could additionally override the original method with an exact signature match. The net result: you achieve something quite similar, functionally.
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 method names are identical (8.4.2 and 8.4.8.1)
the method parameters have identical types, after erasure of generic type parameters (8.4.2 and 8.4.8.1)
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)
Obtain the base type T, on which the method is being invoked.
This is the reference type declared to the compiler and not the runtime type (where a subtype may be substituted).
Determine Method Signature (Compile-Time Determination)
Search the compile-time base type T, for applicable methods matching the name and parameter & return types consistent with the call.
resolve generic parameters for T, either explicitly passed or implicitly inferred from the types of invocation method arguments
stage 1: methods applicable via consistent types/subtypes ('subtyping')
stage 2: methods applicable via automatic boxing/unboxing plus subtyping
stage 3: methods applicable via automatic boxing/unboxing plus subtyping plus variable 'arity' parameters
determine the most specific matching method signature (i.e. the method signature that could successfully be passed to all other matching method signatures); if none: compiler/ambiguity error
Check: Is the Chosen Method Appropriate? (Compile-Time Determination)
Evaluation of Method Invocation (Runtime Determination)
Determine runtime target reference type
Evaluate arguments
Check accessibility of method
Locate method - an exact signature match against the compile-time matched signature
Invoke
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"
Java annotations cannot change the way the compiler generates byte code from your classes. You use annotations to tell the compiler how you think your program should be interpreted, and report errors when compiler's interpretation does not match your intentions. However, you cannot use annotations to force the compiler to produce code with different semantics.
When your subclass declare a method with the same name and parameter count as in its superclass, Java must decide between two possibilities:
You want the method in the subclass to override the method in the superclass, or
You want the method in the subclass to overload the method in the superclass.
If Java allowed foo(Object) to override foo(String) the language would have to introduce an alternative syntax for indicating an intent to overload a method. For example, they could have done it in a way similar to C#'s new and override in method declarations. For whatever reason, however, the designers decided against this new syntax, leaving the language with the rules specified in JLS 8.4.8.1.
Note that the current design lets you implement the functionality of an override by forwarding the call from the overloaded function. In your case, that would mean calling Bar.foo(Object) from Foo.foo(String) like this:
class Foo {
public void foo(String foo) { ... }
}
class Bar extends Foo {
#Override
public void foo(String foo) { this.foo((Object)foo); }
public void foo(Object foo) { ... }
}
Here is a demo on ideone.
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.
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.
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.
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) { ... }
}
Let's say I have the following:
public interface Filter<E> {
public boolean accept(E obj);
}
and
import java.io.File;
import java.io.FilenameFilter;
public abstract class CombiningFileFilter extends javax.swing.filechooser.FileFilter
implements java.io.FileFilter, FilenameFilter {
#Override
public boolean accept(File dir, String name) {
return accept(new File(dir, name));
}
}
As it stands, you can use javac to compile CombiningFileFilter. But, if you also decide to implement Filter<File> in CombiningFileFilter, you get the following error:
CombiningFileFilter.java:9: error: reference to accept is ambiguous,
both method accept(File) in FileFilter and method accept(E) in Filter match
return accept(new File(dir, name));
^
where E is a type-variable:
E extends Object declared in interface Filter
1 error
However, if I make a third class:
import java.io.File;
public abstract class AnotherFileFilter extends CombiningFileFilter implements
Filter<File> {
}
There is no longer a compilation error. The compilation error also goes away if Filter isn't generic:
public interface Filter {
public boolean accept(File obj);
}
Why can't the compiler figure out that since the class implements Filter<File>, the accept method should actually be accept(File) and that there is no ambiguity? Also, why does this error only happen with javac? (It works fine with Eclipse's compiler.)
/edit
A cleaner workaround to this compiler issue than creating the third class would be to add the public abstract boolean accept(File) method in CombiningFileFilter. That erases the ambiguity.
/e2
I am using JDK 1.7.0_02.
As far as I can tell, the compilation error is mandated by the Java Language Specification, which writes:
Let C be a class or interface declaration with formal type parameters A1,...,An, and let C<T1,...,Tn> be an invocation of C, where, for 1in, Ti are types (rather than wildcards). Then:
Let m be a member or constructor declaration in C, whose type as declared is T. Then the type of m (§8.2, §8.8.6) in the type C<T1,...,Tn>, is T[A1 := T1, ..., An := Tn].
Let m be a member or constructor declaration in D, where D is a class extended by C or an interface implemented by C. Let D<U1,...,Uk> be the supertype of C<T1,...,Tn> that corresponds to D. Then the type of m in C<T1,...,Tn> is the type of m in D<U1,...,Uk>.
If any of the type arguments to a parameterized type are wildcards, the type of its members and constructors is undefined.
That is, the method declared by Filter<File> has type boolean accept(File). FileFilter also declares a method boolean accept(File).
CombiningFilterFilter inherits both these methods.
What does that mean? The Java Language Specification writes:
It is possible for a class to inherit multiple methods with override-equivalent (§8.4.2) signatures.
It is a compile time error if a class C inherits a concrete method whose signatures is a subsignature of another concrete method inherited by C.
(That doesn't apply, as neither method is concrete.)
Otherwise, there are two possible cases:
If one of the inherited methods is not abstract, then there are two subcases:
If the method that is not abstract is static, a compile-time error occurs.
Otherwise, the method that is not abstract is considered to override, and therefore to implement, all the other methods on behalf of the class that inherits it. If the signature of the non-abstract method is not a subsignature of each of the other inherited methods an unchecked warning must be issued (unless suppressed (§9.6.1.5)). A compile-time error also occurs if the return type of the non-abstract method is not return type substitutable (§8.4.5) for each of the other inherited methods. If the return type of the non-abstract method is not a subtype of the return type of any of the other inherited methods, an unchecked warning must be issued. Moreover, a compile-time error occurs if the inherited method that is not abstract has a throws clause that conflicts (§8.4.6) with that of any other of the inherited methods.
If all the inherited methods are abstract, then the class is necessarily an abstract class and is considered to inherit all the abstract methods. A compile-time error occurs if, for any two such inherited methods, one of the methods is not return type substitutable for the other (The throws clauses do not cause errors in this case.)
So the "merging" of override-equivalent inherited methods into one method only occurs if one of them is concrete, if all are abstract they remain separate, so all of them are accessible and appliccable to the method invocation.
The Java Language Specification defines what is to happen then as follows:
If more than one member method is both accessible and applicable to a method invocation, it is necessary to choose one to provide the descriptor for the run-time method dispatch. The Java programming language uses the rule that the most specific method is chosen.
The informal intuition is that one method is more specific than another if any invocation handled by the first method could be passed on to the other one without a compile-time type error.
It then defines more specific formally. I'll spare you the definition, but it is worth noting that more specific is not a partial order, as each method is more specific than itself. It then writes:
A method m1 is strictly more specific than another method m2 if and only if m1 is more specific than m2 and m2 is not more specific than m1.
So in our case, where we have several methods with identical signatures, each is more specific than the other, but neither is strictly more specific than the other.
A method is said to be maximally specific for a method invocation if it is accessible and applicable and there is no other method that is applicable and accessible that is strictly more specific.
So in our case, all inherited accept methods are maximally specific.
If there is exactly one maximally specific method, then that method is in fact the most specific method; it is necessarily more specific than any other accessible method that is applicable. It is then subjected to some further compile-time checks as described in §15.12.3.
Sadly, that's not the case here.
It is possible that no method is the most specific, because there are two or more methods that are maximally specific. In this case:
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.
And that, finally, is the salient point: All inherited methods have identical, and therefore override-equivalent signatures. However, the method inherited from the generic interface Filter doesn't have the same erasure as the other ones.
Therefore,
The first example will compile because all methods are abstract, override-equivalent, and have the same erasure.
The second example will not compile, because all methods are abstract, override-equivalent, but their erasure isn't the same.
The third example will compile, because all candicate methods are abstract, override-equivalent, and have the same erasure. (The method with a different erasure is declared in a subclass, and hence not a candidate)
The fourth example will compile, because all methods are abstract, override-equivalent, and have the same erasure.
The last example (repeat abstract method in CombiningFileFilter) will compile, because that method is override-equivalent with all inherited accept methods, and therefore overrides them (note that same erasure is not required for overriding!). So there is only a single appliccable and accessible method, which is therefore the most-specific one.
I can only speculate why the spec requires same erasures in addition to override-equivalence. It might be because, to retain backwards compatibility with non-generic code, the compiler is required to emit a synthetic method with erased signature when a method declaration refers to type parameters. In this erased world, what method can the compiler use as target for the method invocation expression? The Java Language Specification side-steps this issue by requiring that a suitable, shared, erased method declaration is present.
To conclude, javac's behaviour, though far from intuitive, is mandated by the Java Language Specification, and eclipse fails the compatibility test.
There is a method in the FileFilter interface that has the same signature as the one from your concrete interface Filter<File>. They both have the signature accept(File f).
It is an ambiguous reference because the compiler has no way of knowing which of these methods to call in your overridden accept(File f, String name ) method call.
Is method overloading is an example of runtime polymorphism or compile time polymorphism?
There is no operator overloading in Java.
Method overriding enables run-time polymorphism; method overloading enables compile-time polymorphism.
You can read more about this in your textbook.
See also
Polymorphism vs Overriding vs Overloading
Polymorphism: define in two sentences
Wikipedia: Polymorphism in OOP
Method overloading and polymorphism are two completely different concepts. Polymorphism relates to inheritance and determines from which level of the class hierarchy the method is called. Method overloading is creating two methods in the same location with the same name that take different arguments. For instance:
class A{
public void doSomething(){
System.out.println("A method");
}
//method overloaded with different arguments-- no polymorphism
//or inheritance involved!
public void doSomething(int i){
System.out.println("A method with argument");
}
}
class B extends A{
//method polymorphically overridden by subclass.
public void doSomething(){
System.out.println("B method");
}
}
//static type is A, dynamic type is B.
A a = new B();
a.doSomething(); //prints "B method" because polymorphism looks up dynamic type
//for method behavior
Method overloading is example for compile time binding or static binding
method overloading results in polymorphic methods, a method can be defined more than one time having the same name and varying number either method parameters or parameter types (java allows to do this) , when this happens the linkage between method call and actual definition is resolved at compile time thus overloading is called compile time binding or static binding. OO Program language features call it has compile time Polymorphism.
Method overloading is example of complie time binding. Compiler resolves which method needs to be called at compile time only, So it is compile time polymorphism or you can say static binding, But Method overriding is Runtime polymorphism, bcz at run time , depending on the object reference , method call is resolved.