The explanation why we get there two errors is the method reference Integer::toString
can be a reference
- to an instance method of an object of a particular type
- to a static method
Following snippets should demonstrate what the compiler choose in the both cases for Integer::toString
.
instance method i.toString()
would be chosen
static class MyInteger {
int value;
public MyInteger(int i) {
this.value = i;
}
public String toMyString() {
return "instance " + value;
}
}
Stream<MyInteger> stream = ...
stream.map(MyInteger::toMyString).forEach(System.out::println);
/* which would be equivalent to
stream.map(new Function<MyInteger, String>() {
public String apply(MyInteger t) {
return t.toMyString();
}
});
// as method argument for stream.map() the compiler generates
invokevirtual MyInteger.toMyString:()Ljava/lang/String;
*/
static method Integer.toString(i)
would be chosen
static class MyInteger {
int value;
public MyInteger(int i) {
this.value = i;
}
public static String toMyString() {
return "static " + value;
}
}
Stream<MyInteger> stream = ...
stream.map(MyInteger::toMyString)..forEach(System.out::println);
/* which would be equivalent to
stream.map(new Function<MyInteger, String>() {
@Override
public String apply(MyInteger t) {
return MyInteger.toMyString(t);
}
});
// as method argument for stream.map() the compiler generates
invokestatic MyInteger.toMyString:(LMyInteger;)Ljava/lang/String;
*/
In the first pass the parser tries to find a method which could be invoked on an object instance. As both methods toMyString()
and toMyString(MyInteger)
could be invoked on an object of type MyInteger
and both fulfill the requirement for Function<? super T,? extends R>
we get the first error reference to toString is ambiguous
.
(see in the source: com.sun.tools.javac.comp.Resolve.mostSpecific(...)).
In the second pass the parser tries to find a static method toString
. As the reference to the (previously resolved) method toString()
is not static we get the second error non-static method toString() cannot be referenced from a static context
.
(see in the source: com.sun.tools.javac.comp.Resolve.resolveMemberReference(...)).
edit A small example to explain the reason for the two errors. The parser of the javac
cannot know what you intent to do at Integer::toString
. You could mean i.toString()
or Integer.toString(i)
so he do the validation for both cases. It's the way he works also in other situations.
For demonstration take this example:
class Foo {
int x + y = 1;
}
The reported errors are
Scratch.java:2: error: ';' expected
int x + y = 1;
^
Scratch.java:2: error: <identifier> expected
int x + y = 1;
^
In this small snippet the parser also don't know what's your intent. See few possibilities.
int x; y = 1; // missed the semicolon and the declaration for `y`
int x = y = 1; // typo at `+`
int x = y + 1; // swapped `+` and `=` and missed declaration of `y`
... more possibilities exist
In this case the parser also don't stop right after the first error.