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.
Related
From the documentation of java.lang.Class::getDeclaredMethods:
Returns an array containing Method objects reflecting all the declared methods of the class or interface represented by this Class object, including public, protected, default (package) access, and private methods, but excluding inherited methods. The declared methods may include methods not in the source of the class or interface, including bridge methods and other synthetic methods added by compilers.
If this Class object represents a class or interface that has multiple declared methods with the same name and parameter types, but different return types, then the returned array has a Method object for each such method...
AFAIK java doesn't allow a class or an interface to have methods differing only by their return type(return type is not part of a function's signature), so how would this happen?
To understand why this is possible, we need to understand that there are actually two worlds in Java: the Java language world, governed by the JLS, and the Java Virtual Machine world, governed by the JVM Specification.
Those worlds do not always match 1:1. You found one example. Another example are generics. Due to type erasure (oracle.com) the types are replaced with the upper bound through the compiler, thus all type information is lost at runtime. This is the reason why we cannot call .class on a generic parameter T.
Normally as programmers, we only interact with the Java language, and the compiler takes care of transforming the code into Bytecode that is executed on the JVM. Reflection, on the other hand, allows us to interact with objects at runtime. Thus, we get a glimpse into this world. In this specific case, the JVM Specification, $4.7.9.1 (docs.oracle.com) states:
...
A method signature encodes type information about a (possibly generic) method declaration. It describes any type parameters of the method; the (possibly parameterized) types of any formal parameters; the (possibly parameterized) return type, if any; and the types of any exceptions declared in the method's throws clause.
MethodSignature:
[TypeParameters] ( {JavaTypeSignature} ) Result {ThrowsSignature}
Result:
JavaTypeSignature
VoidDescriptor
ThrowsSignature:
^ ClassTypeSignature
^ TypeVariableSignature
...
I have a rather technical question concerning the definition of override-equivalent (JLS 8.4.2) in the context of receiver parameters.
Edit: After posting this question, there was a lot of confusion in the comments about receiver parameters. Many people seemed to believe that having a parameter named this in the code below is illegal and therefore misunderstood the question. Please refer to https://docs.oracle.com/javase/specs/jls/se9/html/jls-8.html#jls-8.4.1 if you do not know this feature. A perhaps more easily understandable explanation can be found here: http://blog.joda.org/2015/12/explicit-receiver-parameters.html. This question is very technical and addresses rather experienced Java developers who are familiar with receiver parameters and know the Java Language Specification (JLS) well.
The term override-equivalent is defined as follows:
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.
Thus, if I understand correctly, the two methods in the following class are not override-equivalent, even though, intuitively, I would have expected them to be:
class A {
void foo(A this) { /* ... */ }
void foo() { /* ... */ }
}
Nevertheless, obviously I cannot declare both methods in the same class, and when I try to do so, the compiler rightfully complains that foo() is already defined.
My question consists of two parts:
Do I understand correctly that the above two methods are not override-equivalent?
If no: What part of the definition did I miss?
If yes: What rule in the JLS formally forbids the above two method definitions to be simultaneously present in the same class, since in this case it cannot be the rule "It is a compile-time error to declare two methods with override-equivalent signatures in a class." (JLS 8.4.2)
Ok, I found out the answer, and I'll just leave it here in the hopes it may someday help someone.
The answer to my two-part question would be:
No, the two methods in the original post are override-equivalent.
The relevant part of the definition is the first sentence of JLS 8.4.2:
Two methods or constructors, M and N, have the same signature if they have the same name, the same type parameters (if any) (§8.4.4), and, after adapting the formal parameter types of N to the the type parameters of M, the same formal parameter types.
I had read that part, but I assumed that a receiver parameter, while special, is also to be considered as (a special kind of) formal parameter. Thus I assumed that receiver parameters were implicitly included in the sentence "(...) after adapting the formal parameter types (...)" (emphasis mine). However, that is not the case. Receiver parameters are not also formal parameters; rather, the definitions of receiver parameter and formal parameter are mutually exclusive. Therefore, receiver parameters are not part of a method signature as defined in the above quote. Since the definition of override-equivalent uses the definition of a method signature, this also means that the two methods in my original post are in fact override-equivalent (because they do have the same signature!).
The relevant section that clarifies that receiver parameters are indeed not also formal parameters comes slightly later in section JLS 8.4.2:
The receiver parameter is an optional syntactic device for an instance method or an inner class's constructor. For an instance method, the receiver parameter represents the object for which the method is invoked. For an inner class's constructor, the receiver parameter represents the immediately enclosing instance of the newly constructed object. Either way, the receiver parameter exists solely to allow the type of the represented object to be denoted in source code, so that the type may be annotated. The receiver parameter is not a formal parameter; more precisely, it is not a declaration of any kind of variable (§4.12.3), it is never bound to any value passed as an argument in a method invocation expression or qualified class instance creation expression, and it has no effect whatsoever at run time.
(emphasis mine)
The statement that the receiver parameter is not a formal parameter got a bit lost in that big blob of text there.
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>
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.
public void add(long... x){}
public void add(Integer... x){}
add(2);
this produces error...why overlaoding is not performed with both widening and boxing?
but overloading without vararg works fine
public void add(long x){}
public void add(Integer x){}
add(2);
here add(long x) will be executed that is widening beats boxing...why not same concept with
var arguments
Java compiler performs three attempts to choose an appropriate method overload (JLS §15.12.2.1):
Phase 1: Identify Matching Arity Methods Applicable by Subtyping
(possible boxing conversions and methods with varargs are ignored)
Phase 2: Identify Matching Arity Methods Applicable by Method
Invocation Conversion
(takes boxing conversion in account, but ignores methods with varargs)
Phase 3: Identify Applicable Variable Arity Methods
(examines all possibilities)
So, with your examples it works as follows:
Without varargs: add(long x) is identified as the only applicable method on the 1st phase (this method is applicable by subtyping since int is a subtype of long, §JLS 4.10.1), so that following phases are not executed.
With varargs: overload resoltion algorithm goes to phase 3, where both methods are identified as applicable, and compiler can't choose the most specific method of them (choosing the most specific method is yet another complex algorithm), therefore it reports ambiguity.
See also:
The Java Language Specification, Seventh Edition
because it is ambiguous.
2 can be Integer as well as long and it can be resolve to both. you made compiler confused whom to invoke :)
5.12.2.2 Choose the Most Specific Method
If more than one method declaration 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 declaration 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.
The precise definition is as follows.
Let m be a name and suppose that there
are two declarations of methods named
m, each having n parameters. Suppose
that one declaration appears within a
class or interface T and that the
types of the parameters are T1, . . .
, Tn; suppose moreover that the other
declaration appears within a class or
interface U and that the types of the
parameters are U1, . . . , Un. Then
the method m declared in T is more
specific than the method m declared in
U if and only if both of the following
are true:
T can be converted to U by method
invocation conversion. Tj can be
converted to Uj by method invocation
conversion, for all j from 1 to n. A
method is said to be maximally
specific for a method invocation if it
is applicable and accessible and there
is no other applicable and accessible
method that is more specific. If there
is exactly one maximally specific
method, then it is in fact the most
specific method; it is necessarily
more specific than any other method
that is applicable and accessible. It
is then subjected to some further
compile-time checks as described in
§15.12.3.
It is possible that no method is the
most specific, because there are two
or more maximally specific methods. In
this case:
If all the maximally specific methods
have the same signature, then: If one
of the maximally specific methods is
not declared abstract, it is the most
specific method. Otherwise, all the
maximally specific methods are
necessarily declared abstract. The
most specific method is chosen
arbitrarily among the maximally
specific methods. However, the most
specific method is considered to throw
a checked exception if and only if
that exception 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.
15.12.2.3 Example: Overloading Ambiguity
Consider the example:
class Point { int x, y; }
class ColoredPoint extends Point { int color; }
class Test {
static void test(ColoredPoint p, Point q) {
System.out.println("(ColoredPoint, Point)");
}
static void test(Point p, ColoredPoint q) {
System.out.println("(Point, ColoredPoint)");
}
public static void main(String[] args) {
ColoredPoint cp = new ColoredPoint();
test(cp, cp); // compile-time error
}
}
This example produces an error at compile time. The problem is that there are two declarations of test that are applicable and accessible, and neither is more specific than the other. Therefore, the method invocation is ambiguous.
If a third definition of test were added:
static void test(ColoredPoint p, ColoredPoint q) {
System.out.println("(ColoredPoint, ColoredPoint)");
}
then it would be more specific than the other two, and the method invocation would no longer be ambiguous.
Read More