I am confused by the following code
class LambdaTest {
public static void main(String[] args) {
Consumer<String> lambda1 = s -> {};
Function<String, String> lambda2 = s -> s;
Consumer<String> lambda3 = LambdaTest::consume; // but s -> s doesn't work!
Function<String, String> lambda4 = LambdaTest::consume;
}
static String consume(String s) { return s;}
}
I would have expected the assignment of lambda3 to fail as my consume method does not match the accept method in the Consumer Interface - the return types are different, String vs. void.
Moreover, I always thought that there is a one-to-one relationship between Lambda expressions and method references but this is clearly not the case as my example shows.
Could somebody explain to me what is happening here?
As Brian Goetz pointed out in a comment, the basis for the design decision was to allow adapting a method to a functional interface the same way you can call the method, i.e. you can call every value returning method and ignore the returned value.
When it comes to lambda expressions, things get a bit more complicated. There are two forms of lambda expressions, (args) -> expression and (args) -> { statements* }.
Whether the second form is void compatible, depends on the question whether no code path attempts to return a value, e.g. () -> { return ""; } is not void compatible, but expression compatible, whereas () -> {} or () -> { return; } are void compatible. Note that () -> { for(;;); } and () -> { throw new RuntimeException(); } are both, void compatible and value compatible, as they don’t complete normally and there’s no return statement.
The form (arg) -> expression is value compatible if the expression evaluates to a value. But there are also expressions, which are statements at the same time. These expressions may have a side effect and therefore can be written as stand-alone statement for producing the side effect only, ignoring the produced result. Similarly, the form (arg) -> expression can be void compatible, if the expression is also a statement.
An expression of the form s -> s can’t be void compatible as s is not a statement, i.e. you can’t write s -> { s; } either. On the other hand s -> s.toString() can be void compatible, because method invocations are statements. Similarly, s -> i++ can be void compatible as increments can be used as a statement, so s -> { i++; } is valid too. Of course, i has to be a field for this to work, not a local variable.
The Java Language Specification §14.8. Expression Statements lists all expressions which may be used as statements. Besides the already mentioned method invocations and increment/ decrement operators, it names assignments and class instance creation expressions, so s -> foo=s and s -> new WhatEver(s) are void compatible too.
As a side note, the form (arg) -> methodReturningVoid(arg) is the only expression form that is not value compatible.
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.
Related
I have the following assignment:
Design a class whose objects have the methods:
setValue which, given a String and an integer value, associates the value with the noun of the variable
getValue which returns the integer value associated with a given String
execute which invokes in sequence a given list of lambda expressions, where each one takes an instance of the class, return nothing, and act on the class through the methods setValue and getValue.
So for the example, after the following:
MyClass instance = new MyClass();
instance.execute(List.of(e -> e.setValue("x", 1),
e -> e.setValue("y", 2)));
instance should contain the values 1 for "x" and 2 for "y".
Here is what I've done so far, I think it's alright:
public class MyClass {
private Map<String,Integer> map;
public int getValue(String name) {return map.get(name);}
public void setValue(String name, int value) {map.put(name, value);}
I am not seeing the way to go for execute. I know I need a functional interface for those lambda expressions, but then I can't solve errors shown by Eclipse when writing instance.execute(List.of(...)) , e.g. "The method of(Object, Object) in the type List is not applicable for the arguments ((<no type> e) -> {}, (<no type> e) -> {}).
How can I make it work?
You need to supply a list of Consumer<MyClass>. A Consumer is a functional interface with a single abstract method called accept which performs the action on the the given argument. Try this.
public void execute(List<Consumer<MyClass>> list) {
list.forEach(cons -> cons.accept(this));
}
The problem with List::of is that you have to either use Java 9 or you can simply do this.
public void execute(Consumer<MyClass> ...list) {
Stream.of(list).forEach(cons -> cons.accept(this));
}
You have it backwards: If you want to call the method for a number of values, those values are the stream. Presuming you have a Map<String, Integer> that represents your "x": 1 dataset, you can do this:
valuesMap.entrySet().stream()
.forEach(entry -> instance.setValue(entry.getKey(), entry.getValue());
Given your problem statement, though, it looks like you're being asked to do something closer to the Visitor pattern. In this case, you need an interface for the lambdas to implement, and there's a built-in one: Consumer<MyClass>. You should get familiar with this one as well as Supplier and Function, since they're very common.
If you declare your execute method to take a list of these, then you can use a for loop or a stream to process them, or use the convenient built-in forEach method:
public void execute(List<Consumer<MyClass>> consumers) {
consumers.forEach(consumer -> consumer.accept(this));
}
I have such code example.
import java.util.LinkedList;
import java.util.List;
import java.util.stream.LongStream;
public class RemovedNumbers {
public static List<long[]> removNb(long n) {
System.out.println(n);
long sum = ((n + 1) * n / 2);
long minMultiplication = sum - 2 * n + 1;
long minCandidate = (long) Math.sqrt(minMultiplication);
LinkedList<long[]> list = new LinkedList<>();
LongStream.rangeClosed(minCandidate, n)
.mapToObj(a -> new long[]{a, calculateB(a, sum)})
.filter(longs -> longs[0] > longs[1])
.filter(longs -> longs[1] <= n)
.filter(longs -> longs[0] * longs[1] == sum - longs[0] - longs[1])
.forEach(longs -> addArrays(list, longs));
return list;
}
private static long calculateB(long a, long sum) {
return (sum - a) / (a + 1);
}
private static void addArrays(final LinkedList<long[]> list, final long[] longs) {
list.addFirst(new long[]{longs[1], longs[0]});
list.add(longs);
}
}
This code is complicated for me in LongStream part.
I don't get some points, so I need a help:
I examine LongStream class.
This class uses four methods: rangeClosed, mapToObj, filter, forEach (their description I found on Java docs). Unfortunately, now I am starting to examine java 1.8 version, so I can't understand how it works and what's happens.
Where is appeared "a" in mapToObj? What is it? I don't see var "a" declaration in previous part of code.
As I've got lambda is made by such scheme: (arguments) -> (body). So the "a" is an argument, "new long[]..." - is a body. This part isn't causes any question for me. But the next one, whereis "longs" - argument, "longs[0] > longs[1]" - body, causes some questions. What is the var "longs"? It hasn't declaration in the past! HOW it appears? How it works?
Am I right that LongStream class can be writes in one line? Like: LongStream.rangeClosed().filter().filter().filter().forEach(); ?
Am I right that all methods execute consequently? By each other? The first rangeClosed, then mapToObj, then filter... or is there another order?
Thanks a lot!
Your third point kind of answers your second point - a is the parameter of the lambda expression passed to mapToObj.
If you can understand that, then your fourth point should be easy to understand as well. longs is the parameter for the lambda expression passed to filter. Remember that you can name your parameter names whatever you like. I guess the reason why the author of the code renamed the parameter to longs is because in the previous line, each long in the stream is mapped into a long[], so now it's a stream of long arrays.
Am I right that LongStream class can be writes in one line?
Yes, but you would end up with a super long line of code, so we almost never do that.
Am I right that all methods execute consequently? By each other? The first rangeClosed, then mapToObj, then filter... or is there another order?
The methods get called in that order, but the operations they do won't run immediately. This is the cool part of streams. The longs will only be mapToObj'ed and filter'ed when you do forEach, a terminal operation. In other words, mapToObj and filter are kind of like saying "this is what this stream should do..." and when you do forEach, you are saying "now do it!"
If you still don't get what streams are doing, try to think of them as a production line in a factory. At the start, you have longs on the conveyer belts. And then they pass through a machine, transforming each of them into a long[]. After that, they pass through three filters. These filters will push them off the conveyer belt unless the long arrays fulfil some condition.
EDIT:
If you want to write this code without lambdas, you can write it with anonymous classes instead:
LongStream.rangeClosed(minCandidate, n)
.mapToObj(new LongFunction<long[]>() {
#Override
public long[] apply(long a) {
return new long[]{a, calculateB(a, sum)};
}
})
.filter(new Predicate<long[]>() {
#Override
public boolean test(long[] longs) {
return longs[0] > longs[1] &&
longs[1] <= n &&
longs[0] * longs[1] == sum - longs[0] - longs[1];
}
})
.forEach(new Consumer<long[]>() {
#Override
public void accept(long[] longs) {
addArrays(list, longs);
}
});
Each lambda expression implements a functional interface, or to be more specific, it implements the single abstract method of that functional interface.
Therefore, in a -> new long[]{a, calculateB(a, sum)}, a is the argument of the method implemented by the functional interface. Since mapToObj accepts an argument of type LongFunction, this lambda expression implements the R apply(long value) method of that interface, which means that lambda expression can also be written as (long a) -> new long[]{a, calculateB(a, sum)}.
This mapToObj call transforms the LongStream to a Stream<long[]>, so the lambda expression of the following filter call - longs -> longs[0] > longs[1] can also be written as (long[] longs) -> longs[0] > longs[1] - it implements the functional interface Predicate<long[]>, which means it implements boolean test(long[] t).
Yes, you can write this entire stream pipeline in a single line, but it would be more readable split into multiple lines.
Am I right that all methods execute consequently? By each other? The first rangeClosed, then mapToObj, then filter... or is there another order
Not exactly. While each intermediate method produces an output used as input to the next method, the evaluation of these methods only begins once the terminal operation - forEach in this case - is executed. And these operations don't necessarily process all the elements of the Stream. For example, if the terminal operation would be firstFirst() instead of forEach, the pipeline would only process enough elements until the first element that passes all the filters is found.
Am I right that LongStream class can be writes in one line?
What you're witnessing here is method chaining. This is where method after method can get chained to eachother. This can be done for almost all classes.
Everything else is pretty much answered by Sweeper.
3 and 4:
you are tryng to understand how lambda work so I'll break it down your code for you:
// this return a LongStream obj
LongStream.rangeClosed(minCandidate, n)
// so with point notation you can access to one of the method in LongStream
// matToObj in this case.
.mapToObj(a -> new long[]{a, calculateB(a, sum)})
what is a? What ->? what the other stuff?
MapToObj takes a IntFunction mapper argument and a is a declaration of that type on the fly this is why you didin't see it before in the code.
the arrow indicates that the right site is the lamba expression, if you have a inline operation you can omit the return statement and you can not include {} brackets so imagine that statement like a return statement.
With lamba functions you can easily create chains of operation this is why you have many functions called one after another. You have to keep in mind that the next function takes as argument an object type that is of the same type of the return type of the previous function.
The following code surprisingly is compiling successfully:
Consumer<String> p = ""::equals;
This too:
p = s -> "".equals(s);
But this is fails with the error boolean cannot be converted to void as expected:
p = s -> true;
Modification of the second example with parenthesis also fails:
p = s -> ("".equals(s));
Is it a bug in Java compiler or is there a type inference rule I don't know about?
First, it's worth looking at what a Consumer<String> actually is. From the documentation:
Represents an operation that accepts a single input argument and
returns no result. Unlike most other functional interfaces, Consumer
is expected to operate via side-effects.
So it's a function that accepts a String and returns nothing.
Consumer<String> p = ""::equals;
Compiles successfully because equals can take a String (and, indeed, any Object). The result of equals is just ignored.*
p = s -> "".equals(s);
This is exactly the same, but with different syntax. The compiler knows not to add an implicit return because a Consumer should not return a value. It would add an implicit return if the lambda was a Function<String, Boolean> though.
p = s -> true;
This takes a String (s) but because true is an expression and not a statement, the result cannot be ignored in the same way. The compiler has to add an implicit return because an expression can't exist on its own. Thus, this does have a return: a boolean. Therefore it's not a Consumer.**
p = s -> ("".equals(s));
Again, this is an expression, not a statement. Ignoring lambdas for a moment, you will see the line System.out.println("Hello"); will similarly fail to compile if you wrap it in parentheses.
*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.
**From the spec (thanks, Eugene):
A lambda expression is congruent with a [void-producing] function type if ...
the lambda body is either a statement expression
(§14.8)
or a void-compatible block.
I think the other answers complicate the explanation by focusing on lambdas whereas their behavior in this case is similar to the behavior of manually implemented methods. This compiles:
new Consumer<String>() {
#Override
public void accept(final String s) {
"".equals(s);
}
}
whereas this does not:
new Consumer<String>() {
#Override
public void accept(final String s) {
true;
}
}
because "".equals(s) is a statement but true is not. A lambda expression for a functional interface returning void requires a statement so it follows the same rules as a method's body.
Note that in general lambda bodies don't follow exactly the same rules as method bodies - in particular, if a lambda whose body is an expression implements a method returning a value, it has an implicit return. So for example, x -> true would be a valid implementation of Function<Object, Boolean>, whereas true; is not a valid method body. But in this particular case functional interfaces and method bodies coincide.
s -> "".equals(s)
and
s -> true
don't rely on same function descriptors.
s -> "".equals(s) may refer either String->void or String->boolean function descriptor.
s -> true refers to only String->boolean function descriptor.
Why ?
when you write s -> "".equals(s), the body of the lambda : "".equals(s) is a statement that produces a value.
The compiler considers that the function may return either void or boolean.
So writing :
Function<String, Boolean> function = s -> "".equals(s);
Consumer<String> consumer = s -> "".equals(s);
is valid.
When you assign the lambda body to a Consumer<String> declared variable, the descriptor String->void is used.
Of course, this code doesn't make much sense (you check the equality and you don't use the result) but the compiler doesn't care.
It is the same thing when you write a statement : myObject.getMyProperty() where getMyProperty() returns a boolean value but that you don't store the result of it.
when you write s -> true, the body of the lambda : true is a single expression .
The compiler considers that the function returns necessarily boolean.
So only the descriptor String->boolean may be used.
Now, come back to your code that doesn't compile.
What are you trying to do ?
Consumer<String> p = s -> true;
You cannot. You want to assign to a variable that uses the function descriptor Consumer<String> a lambda body with the String->void function descriptor.
It doesn't match !
I am confused by the following code
class LambdaTest {
public static void main(String[] args) {
Consumer<String> lambda1 = s -> {};
Function<String, String> lambda2 = s -> s;
Consumer<String> lambda3 = LambdaTest::consume; // but s -> s doesn't work!
Function<String, String> lambda4 = LambdaTest::consume;
}
static String consume(String s) { return s;}
}
I would have expected the assignment of lambda3 to fail as my consume method does not match the accept method in the Consumer Interface - the return types are different, String vs. void.
Moreover, I always thought that there is a one-to-one relationship between Lambda expressions and method references but this is clearly not the case as my example shows.
Could somebody explain to me what is happening here?
As Brian Goetz pointed out in a comment, the basis for the design decision was to allow adapting a method to a functional interface the same way you can call the method, i.e. you can call every value returning method and ignore the returned value.
When it comes to lambda expressions, things get a bit more complicated. There are two forms of lambda expressions, (args) -> expression and (args) -> { statements* }.
Whether the second form is void compatible, depends on the question whether no code path attempts to return a value, e.g. () -> { return ""; } is not void compatible, but expression compatible, whereas () -> {} or () -> { return; } are void compatible. Note that () -> { for(;;); } and () -> { throw new RuntimeException(); } are both, void compatible and value compatible, as they don’t complete normally and there’s no return statement.
The form (arg) -> expression is value compatible if the expression evaluates to a value. But there are also expressions, which are statements at the same time. These expressions may have a side effect and therefore can be written as stand-alone statement for producing the side effect only, ignoring the produced result. Similarly, the form (arg) -> expression can be void compatible, if the expression is also a statement.
An expression of the form s -> s can’t be void compatible as s is not a statement, i.e. you can’t write s -> { s; } either. On the other hand s -> s.toString() can be void compatible, because method invocations are statements. Similarly, s -> i++ can be void compatible as increments can be used as a statement, so s -> { i++; } is valid too. Of course, i has to be a field for this to work, not a local variable.
The Java Language Specification §14.8. Expression Statements lists all expressions which may be used as statements. Besides the already mentioned method invocations and increment/ decrement operators, it names assignments and class instance creation expressions, so s -> foo=s and s -> new WhatEver(s) are void compatible too.
As a side note, the form (arg) -> methodReturningVoid(arg) is the only expression form that is not value compatible.
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.
I have a stream of files that I want to filter based on the ending of the file name:
public Stream<File> getFiles(String ending) throws IOException {
return Files.walk(this.path)
.filter(Files::isRegularFile)
.map(Path::toFile)
.filter(file -> file.getName().endsWith(ending));
}
While the lambda in the last line is not bad, I thought I could use method references there as well, like so:
.filter(File::getName.endsWith(ending));
Or alternatively wrapped in parentheses. However, this fails with The target type of this expression must be a functional interface
Can you explain why this doesn't work?
Can you explain why this doesn't work?
Method references are syntactical sugar for a lambda expression. For example, the method reference File::getName is the same as (File f) -> f.getName().
Lambda expressions are "method literals" for defining the implementation of a functional interface, such as Function, Predicate, Supplier, etc.
For the compiler to know what interface you are implementing, the lambda or method reference must have a target type:
// either assigned to a variable with =
Function<File, String> f = File::getName;
// or assigned to a method parameter by passing as an argument
// (the parameter to 'map' is a Function)
...stream().map(File::getName)...
or (unusually) cast to something:
((Function<File, String>) File::getName)
Assignment context, method invocation context, and cast context can all provide target types for lambdas or method references. (In all 3 of the above cases, the target type is Function<File, String>.)
What the compiler is telling you is that your method reference does not have a target type, so it doesn't know what to do with it.
File::getName is a method reference and String::endsWith is as well. However they cannot be chained together. You could create another method to do this
public static Predicate<File> fileEndsWith(final String ending) {
return file -> file.getName().endsWith(ending);
}
and then use it
.filter(MyClass.fileEndsWith(ending))
This doesn't buy you much if you're not re-using it though.
A couple of helpers might assist in providing some semblance of what you wish for. Using the helpers below, you can replace your lambda with an expression containing method references, like this:
// since your predicate is on the result of a function call, use this to create a predicate on the result of a function
public static <A,B> Predicate<A> onResult(Function<A,B> extractor, Predicate<B> predicate){
return a -> predicate.test(extractor.apply(a));
}
// since your predicate involves an added parameter, use this to reduce the BiPredicate to a Predicate with one less parameter
public static <T,U> Predicate<T> withParam(BiPredicate<T,U> pred, U param){
return t -> pred.test(t,param);
}
public Stream<File> getFiles(String ending) throws IOException {
return Files.walk(Paths.get("."))
.filter(Files::isRegularFile)
.map(Path::toFile)
.filter(onResult(File::getName, withParam(String::endsWith, ending)));
}