I tried everything to get 100% of coverage on this lambda method but no matter what I do I don't get it.
private String createMessage(List<FieldError> erros) {
return erros.stream().collect(Collectors.toMap(FieldError::getField, FieldError::getDefaultMessage, (keyOld, keyNew) -> keyOld)).toString();
}
The uncovered code is the implementation of a lambda expression. You only have one lambda expression (keyOld, keyNew) -> keyOld, which means that that code doesn't get executed.
The lambda expression is the 3rd argument to Collectors.toMap(), i.e. BinaryOperator<U> mergeFunction, which is documented as "a merge function, used to resolve collisions between values associated with the same key".
If there are no collisions in the data, the lambda expression won't get executed, so make sure you test the code with data where the erros list contains 2 or more elements with the same getField() value.
Related
Sorry, it seems to be very basic in functional programming but I am not getting this idea. Actually I have a method in my code which consumes a method and another param as a parameter.
private <R> CompletableFuture<R> retryRequest(Supplier<CompletableFuture<R>> supplier, int maxRetries)
I want to call this function and pass another method(anOtherMethod) which taking one integer parameter:
CompletableFuture<Boolean> retry = this.retryRequest(this:: anOtherMethod, 2);
Not getting this how I can call this retryRequest and give anOtherMethod(123)?
I know it can work like this:
CompletableFuture<Boolean> retry = this.retryRequest(()-> anOtherMethod(123), 2);
You cannot instantiate a lambda with a specific captured value like 123 in the pure method reference variant.. You need to write the explicit lambda version with arrow, if you want to pass captured values other than the instance to execute the method on. Read more on capturing values in lambdas in this answer: Enhanced 'for' loop and lambda expressions
The only exception is an object, which itself becomes the first parameter.
Assume a signature that expects a Consumer of a String:
public void something(Consumer<String> job) {
...
The above signature will enable you to write the following calls:
String myString = " Hey Jack ";
something(myString::trim);
something(s -> s.trim());
Both do the same, and this is maybe unintuitive, because one takes an argument (the instance reference myString) and one seem not to (but it actually does, too). This works, because the compiler tries two possible resolutions for a lambda method reference (the above version with ::). On one hand, the compiler can apply signatures, as if the called method did not have any parameters, and none need passing. This is the case for myString.trim. But the compiler will also check, whether there is a static method String.trim(myString) (which luckiely there is not). If you wanted to call a static method without any parameters, then you'd have to call the class identifier with the function reference like so:
something(String::trim); // this version of trim does not exist.
This is sometimes even a problem, because if a class offers a static version of a method and an instance-related one, you get ambiguity:
public void somethingElse(Function<Integer, String> transformation) {...}
// This will not compile:
somethingElse(Integer::toString);
The above example will not compile, because the toString method exists twice, once as static Integer.toString(someInt) and once as instance related someInteger.toString().
Are the lambda expressions evaluated at the place where we write them or in any other class of Java?
For example :
Stream<Student> absent = students.values().stream().filter(s -> !s.present());
Will the above lambda expression passed to the filter method be executed immediately in a given class where the code is written OR in another class and will it take some more time (in terms of nano seconds) than if the code was written in conventional coding style prior to Java 8?
When you compile your sources, the compiler will insert an invokedynamic byte code instruction for the lambda expression that you use. The actual implementation (which in your case is a Predicate) will be created at runtime via ASM. It will not even be present on hard disk when you run it - meaning the class is generated in memory, there will be no .class file for Predicate. That's a big difference between an anonymous class for example - that will generate a class file when you compile it.
You can see the generated file for the Predicate if you run your example with :
-Djdk.internal.lambda.dumpProxyClasses=/Your/Path/Here
Otherwise Eran's answer is correct, Streams are driven by the terminal operation, if such is not present nothing gets executed. You should absolutely read the excellent Holger's answer about even more interesting differences.
The body of the lambda expression passed to the filter method in your example won't be executed at all, since filter is an intermediate operation, which only gets executed for Streams that end in a terminal operation, such as collect, forEach, etc...
If you add a terminal operation, such as collecting the elements of the Stream to a List:
List<Student> absent = students.values().stream().filter(s -> !s.present()).collect(Collectors.toList());
the body of the lambda expression will be executed for each element of your Stream, in order for the terminal operation to be able to produce its output.
Note that this behavior would not change if you passed an anonymous class instance or some other implementation of the Predicate interface to your filter method instead of the lambda expression.
The expressions are lazy evaluated, which means they'll only actually be evaluated when you actually try to 'terminate' the stream - i.e. use an operation that takes a stream but returns something else, like collect, min, max, reduce, etc. Operations which take a stream as input and return a stream as output are usually lazy.
Lambda expressions are essentially objects with a single method, so they're evaluated whenever that method is called.
In your particular case they're never evaluated. A Stream does not evaluate the expressions until you call a terminating operation (collect, findAny, etcetera)
I want to replace lambda expression by method reference in the below example :
public class Example {
public static void main(String[] args) {
List<String> words = Arrays.asList("toto.", "titi.", "other");
//lambda expression in the filter (predicate)
words.stream().filter(s -> s.endsWith(".")).forEach(System.out::println);
}
}
I want to write a something like this :
words.stream().filter(s::endsWith(".")).forEach(System.out::println);
is it possible to transform any lambda expression to method reference.
There is no way “to transform any lambda expression to method reference”, but you can implement a factory for a particular target type, if this serves recurring needs:
public static <A,B> Predicate<A> bind2nd(BiPredicate<A,B> p, B b) {
return a -> p.test(a, b);
}
with this, you can write
words.stream().filter(bind2nd(String::endsWith, ".")).forEach(System.out::println);
but actually, there’s no advantage. Technically, a lambda expression does exactly what you want, there’s the minimum necessary argument transformation code, expressed as the lambda expression’s body, compiled into a synthetic method and a method reference to that synthetic code. The syntax
s -> s.endsWith(".") also is already the smallest syntax possible to express that intent. I doubt that you can find a smaller construct that would still be compatible with the rest of the Java programming language.
You can use selectWith() from Eclipse Collections. selectWith() takes a Predicate2 which takes 2 parameters instead of a Predicate. The second parameter to selectWith() gets passed as the second parameter to the Predicate2 every time it's called, once per item in the iterable.
MutableList<String> words = Lists.mutable.with("toto.", "titi.", "other");
words.selectWith(String::endsWith, ".").each(System.out::println);
By default Eclipse Collections is eager, if you want to iterate lazily then you can use asLazy()
words.asLazy().selectWith(String::endsWith, ".").each(System.out::println);
If you can't change from List:
List<String> words = Arrays.asList("toto.", "titi.", "other");
ListAdapter.adapt(words).selectWith(String::endsWith, ".").each(System.out::println);
Eclipse Collections' RichIterable has several other *With methods which work well with method references, including rejectWith(), partitionWith(), detechWith(), anySatisfyWith(), allSatisfyWith(), noneSatisfyWith(), collectWith()
Note: I am a contributor to Eclipse Collections.
I just performed a quick experiment in Eclipse.
public class StackTractTest {
static class Nasty {
public Integer toInt() {
if (1 == 1) throw new RuntimeException();
return 1;
}
}
#Test
public void methodReference() {
Stream.of(new Nasty())
.map(Nasty::toInt)
.findFirst();
}
#Test
public void lambda() {
Stream.of(new Nasty())
.map(n -> n.toInt())
.findFirst();
}
}
When the method-reference test fails, the trace begins
java.lang.RuntimeException
at com.example.StackTractTest$Nasty.toInt(StackTractTest.java:11)
at com.example.StackTractTest$$Lambda$1/1681433494.apply(Unknown Source)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
There is no reference back to the line on which the method reference is used although the end of the trace (not shown) does link back to line with findFirst on.
While the lamdba stacktrace begins
java.lang.RuntimeException
at com.example.StackTractTest$Nasty.toInt(StackTractTest.java:11)
at com.example.StackTractTest.lambda$0(StackTractTest.java:26)
at com.example.StackTractTest$$Lambda$1/1681433494.apply(Unknown Source)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
Which correctly identifies the lambda was used on line 26.
Is this a peculiarity of the Eclipse compiler or is this a general disadvantage of using method references that should be considered when choosing between them and a lambda?
No, this is how it is currently implemented.
Quoting a paper written by Brian Goetz about the translation of lambda expressions:
When the compiler encounters a lambda expression, it first lowers (desugars) the lambda body into a method whose argument list and return type match that of the lambda expression
...
Method references are treated the same way as lambda expressions, except that most method references do not need to be desugared into a new method; we can simply load a constant method handle for the referenced method and pass that to the metafactory.
The only difference between your two stacktraces is that the one with the explicit lambda has this line added:
at com.example.StackTractTest.lambda$0(StackTractTest.java:26)
It is because the lambda was translated by javac into a newly generated method, and you can actually see in the stacktrace that this new method was lambda$0.
With a method-reference, it is not necessary to generate a new method because it directly references an existing method.
No - in fact you get more clarity.
The fact that the at com.example.StackTractTest.lambda$0(StackTractTest.java:26) line appears in the lambda version is reminding you that a lambda is created for this technique while using a method reference does not create anything extra.
The lambda is created at run-time, the method-reference can be constructed at compile time.
This code sample
Collection<Number> values = transform(
getValuatedObjects(),
input -> getValueProvider().apply(input).getValue());
violates the Sonarqube rule:
Lambdas should be replaced with method references
Is it a sonar bug?
Or can I really use a method reference?
You can’t replace the lambda input -> getValueProvider().apply(input).getValue() with a method reference without changing the semantics.
A method reference replace a single method invocation, so it can’t simply replace a lambda expression consisting of more than one method invocation.
A lambda expression of the form input -> getValueProvider().apply(input) could be replaced by getValueProvider()::apply if, and only if, the evaluation time of getValueProvider() does not matter as in the lambda form the method is invoked on each lambda body evaluation while for the method reference it is invoked only once and the result captured.
This is similar to the difference between x -> System.out.println(x) and System.out::println where reading the contents of the field System.out happens at different times but usually it doesn’t matter. But you should be aware of the difference.
In your example, a third method getValue() is invoked. The only way to express that with method references needs a functional interface like Function which has methods like andThen and/or compose. However, the way Java 8 works, that would require casting the first method reference to the target interface to invoke the combining method which would be by no way easier to read that the lambda expression you have now: ((Function<X,Y>)getValueProvider()::apply).andThen(Y::getValue) where Y is the type, apply(input) returns.
Note that the rule says “Replace lambdas with method references when possible” which gives you room to say, “well, here it is impossible”, however, I’m not sure how much you can call it a “rule” then…
list.stream().sorted().collect(Collectors.toList()).forEach(element ->
operate(element));
replace the above lambda with a method reference.
list.stream().sorted().collect(Collectors.toList()).forEach(this::operate);
if you are coding in java 8 you can use method reference in place of lambda expression for code readable
List<Integer> list = Arrays.asList(1,2,3,4,5);
replace this lambda with a method reference
strList.stream().sorted().collect(Collectors.toList()).forEach(s -> System.out.println(s));
Replace
strList.stream().sorted().collect(Collectors.toList()).forEach(System.out::println);
List<String> inputStringList = List.of("A", "B", "C", "D");
List<String> outputStringList = List.of("C", "D", "E", "F");
assertTrue(outputStringList.stream().anyMatch(inputStringList::contains));
assertTrue(outputStringList.stream().allMatch(inputStringList::contains));
This is a sample code to check if content of List is matching against another List using method reference.