Why does a Java method reference with return type match the Consumer interface?
consume(String)
method matches Consumer<String>
interface, because it consumes a String
- the fact that it returns a value is irrelevant, as - in this case - it is simply ignored. (Because the Consumer
interface does not expect any return value at all).
It must have been a design choice and basically a utility: imagine how many methods would have to be refactored or duplicated to match needs of functional interfaces like Consumer
or even the very common Runnable
. (Note that you can pass any method that consumes no parameters as a Runnable
to an Executor
, for example.)
Even methods like java.util.List#add(Object)
return a value: boolean
. Being unable to pass such method references just because that they return something (that is mostly irrelevant in many cases) would be rather annoying.
why BiConsumer Funcational Interface accepts methods having return type
BiConsumer<String, String> consumer= (s,t) -> {
String result = roles.put(s,t); // result is ignored
return;
}
Also called special void compatibility rule in the JLS
.
How Java 8+ infers method reference as consumer?
The Java-8 compiler looks at the method declarations involved:
public void forEach(Consumer<? super T>
in interfaceIterable<T>
public void accept(T t)
in interfaceConsumer<T>
public static String test(String)
in your classRunner
From all these it concludes that Consumer<String>
will fit.
Hence, it expands the method reference Runner::test
to an implementation of interface Consumer<String>
which delegates
to the method test(String)
.
The line
Arrays.asList(testArgs).forEach(Runner::test);
is expanded to something equivalent to this:
Arrays.asList(testArgs).forEach(new Consumer<String>() {
@Override
public void accept(String t) {
test(t);
}
});
Actually it does not exactly the above, but uses some JVM optimization
to avoid the anonymous class overhead.
But diving into these JVM details would lead too far here.
Method Reference - passing Function to method with Consumer argument
Two things here, lambda expressions are poly expressions - they are inferred by the compiler using their context (like generics for example).
When you declare consume(Holder::getHolded);
, compiler (under the so-called special void compatibility rule) will infer it to Consumer<Holder>
.
And this might not look obvious, but think of a simplified example. It is generally more than ok do call a method and discard it's return type, right? For example:
List<Integer> list = new ArrayList<>();
list.add(1);
Even if list.add(1)
returns a boolean, we don't care about it.
Thus your example that works can be simplified to:
consume(x -> {
x.getHolded(); // ignore the result here
return;
});
So these are both possible and valid declarations:
Consumer<Holder> consumer = Holder::getHolded;
Function<Holder, String> function = Holder::getHolded;
But in this case we are explicitly telling what type is Holder::getHolded
,, it's not the compiler inferring, thus consume(getHolded);
fails, a Consumer
!= Function
after all.
Why does this method returning a lambda depend on the return type of the interface method?
Your function type is Function<Person, Comparable>
, which means fun.apply(p1)
returns a Comparable
. This is an interface defined in the Java standard library, which declares a method named compareTo
which returns an int
, so therefore your lambda returns an int
.
This means if you define your functional interface as having a single abstract method returning an int
, then your lambda conforms to the functional interface because it does return int
. But if you define that abstract method as returning a String
, then the lambda does not conform to the functional interface, because it does not return a String
.
In the latter case, it is a type error for your comparing
method to return that lambda, since the signature of comparing
says it should return an instance implementing your functional interface, but the lambda does not conform to that functional interface due to the lambda having the wrong return type.
Why does the following casting with method reference not produce a compilation error?
From the spec:
If the body of a lambda is a statement expression (that is, an
expression that would be allowed to stand alone as a statement), it is
compatible with a void-producing function type; any result is simply
discarded.
Same is true for method references.
It's more flexible that way. Suppose it was a compiler error to not use a return value when you called a method normally - that would be incredibly annoying. You'd end up having to use fake variables you didn't care about in some cases.
public class SomeClass
{
public static int someFunction(int a) {
return a;
}
public static void main(String[] args) {
someFunction(3); // "error" - ignoring return type
int unused = someFunction(3); // "success"
}
}
If you want a the full formal definition of what is acceptable, see 15.13.2. Type of a Method Reference.
Java consumer with return
Your Consumer<Resource> block = resource -> resource.op1().op2();
is equivalent to:
Consumer<Resource> block = new Consumer<Resource>() {
@Override
public void accept(Resource resource) {
resource.op1().op2(); // there is no return statement
}
};
Why does functional interface with void return-type method accept any return-type method?
Although
interface J
declares it has avoid
function,Test::foo
returns anObject
.
It's inaccurate to say that Test::foo
returns something. In different contexts, it could mean different things.
Supplier<Object> a = Test::foo;
J b = Test::foo;
Runnable c = Test::foo;
It's more accurate to say that Test::foo
can represent a target type the functional method of which returns either void
or Object
.
It's an example of expression statement (jls-14.8).
If the target type's function type has a
void
return, then the lambda body is either a statement expression (§14.8) or a void-compatible block (§15.27.2).
...
An expression statement is executed by evaluating the expression; if the expression has a value, the value is discarded.
Related Topics
Why Do People Still Use Primitive Types in Java
How to Specify Jackson to Only Use Fields - Preferably Globally
Why Does Java Prohibit Static Fields in Inner Classes
Java Generics: List, List<Object>, List<>
What Is This: [Ljava.Lang.Object;
Random Weighted Selection in Java
Java - Best Approach to Parse Huge (Extra Large) JSON File
Nosuchmethoderror in Javax.Persistence.Table.Indexes()[Ljavax/Persistence/Index
Why Does a Java Method Reference with Return Type Match the Consumer Interface
Sqlexception: No Suitable Driver Found for Jdbc:Derby://Localhost:1527
What Causes "'Void' Type Not Allowed Here" Error
The JPA Hashcode()/Equals() Dilemma
What Does the Java Assert Keyword Do, and When Should It Be Used