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.
Related
I’m storing references to BiConsumers<Integer, X> adapted to Consumer<Integer>:
public void setConsumer(BiConsumer<Integer, X> consumer) {
fieldConsumer = integer -> consumer.accept(integer, fieldSubject);
}
But I need 2 of them, so I changed the code to use an array:
private Consumer<Integer>[] fieldConsumers;
public MyClass(int numberOfConsumers) {
Consumer<Integer> consumer = integer -> {};
fieldConsumers= (Consumer<Integer>[]) Array.newInstance(consumer.getClass(), numberOfObservers);
}
public void addConsumer(int consumerIndex, BiConsumer<Integer, X> consumer) {
// Offending line
fieldConsumers[consumerIndex] = responseType-> consumer.accept(responseType, fieldSubject);
}
So that the callback can be triggered with a:
for (Consumer<Integer> consumer: fieldConsumers) {
consumer.accept(responseType);
}
I got the error:
java.lang.ArrayStoreException:
on this line:
fieldConsumers[consumerIndex] = responseType-> consumer.accept(responseType, fieldSubject);
Now, If you are still reading this, I have one more question:
Am I still holding reference to outside Consumers if I do it this way, as opposed to using the old fieldConsumers.add(consumer) where fieldConsumers is a List<BiConsumer<Integer, X>> ?
You used Array.newInstance(consumer.getClass(), numberOfObservers) to create the Consumer<Integer>[] array. But consumer.getClass() returns the actual class of the object you’re invoking the method on, which is always an implementation class of the interface. An array of this element type can only hold objects of the same concrete class, not arbitrary implementations of the interface.
This is not different to, e.g.
CharSequence cs = "hello";
CharSequence[] array = (CharSequence[]) Array.newInstance(cs.getClass(), 1);
array[0] = new StringBuilder();
Here, cs has the type CharSequence and the reflective array creation appears to create an array of type CharSequence[], so storing a StringBuilder should be possible. But since cs.getClass() returns the actual implementation class String, the array is actually of type String[], hence, the attempt to store a StringBuilder produces an ArrayStoreException.
In case of lambda expressions, things get slightly more complicated, as the actual implementation classes of the functional interface are provided at runtime and intentionally unspecified. You used the lambda expression integer -> {} for the array creation in the constructor, which evaluated to a different implementation class than the responseType-> consumer.accept(responseType, fieldSubject) within the addConsumer method, in this particular runtime.
This behavior is in line with this answer describing the behavior of the most commonly used environment. Still, other implementations could exhibit different behavior, e.g. evaluate to the same implementation class for a particular functional interface for all lambda expressions. But it’s also possible that multiple evaluations of the same lambda expression produce different classes.
So the fix is to use the intended interface element type, e.g.
fieldConsumers=(Consumer<Integer>[])Array.newInstance(Consumer.class, numberOfObservers);
But there is no need for a reflective array creation at all. You can use:
fieldConsumers = new Consumer[numberOfObservers];
You can not write new Consumer<Integer>[numberOfObservers], as generic array creation is not allowed. That’s why the code above uses a raw type. Using Reflection instead wouldn’t improve the situation, as it is an unchecked operation in either case. You might have to add #SuppressWarnings for it. The cleaner alternative is to use a List<Consumer<Integer>>, as it shields you from the oddities of arrays and generics.
It’s not clear what you mean with “reference to outside Consumers” here. In either case, you have references to Consumer implementations capturing references to BiConsumer implementations you received as arguments to addConsumer.
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().
I have examined the following snippet:
public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
Map<Object, Boolean> computed = new ConcurrentHashMap<>();/*IS THIS LINE CALLED ONCE ON STREAM->FILTER NOT MATTER HOW LONG THE STREAM IS*/
return t -> {return computed.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;};
}
private void test(){
final long d = Stream.of("JOHN","STEPHEN","ORTIZ","RONDON")
.filter(distinctByKey(Function.identity()))
.count();
System.out.println("d = " + d);
}
This code is not mine. I know that using a ConcurrentMap is not the right choice in this example and I should use ConcurrentMap instead of Map in this case, but this is not my concern now.
I thought that the distinctByKey method is called or interpreted in each iteration of the Stream. I mean the Map being instantiated in each turn, but it's not!
Is the body of the Predicate method called only once?
In the Stream iteration, is this an assertion?
Because when I try the following code:
final Function<String,Integer>a = (name)->name.length();
System.out.println(distinctByKey(a).test("JOHN"));
System.out.println(distinctByKey(a).test("STEPHEN"));
System.out.println(distinctByKey(a).test("ORTIZ"));
System.out.println(distinctByKey(a).test("RONDON"));
I can see that the body of the method is indeed called in each line. What makes the body of the filter to only be called once?
When you call .filter(distinctByKey(Function.identity())), the argument passed to filter() is evaluated. That's the only time distinctByKey(Function.identity()) is executed and returns an instance of Predicate<String>.
That Predicate is then evaluated (i.e. it's test() method is executed) multiple times, each time for a different element of the Stream.
To make your last snippet behave similar to the Stream pipeline, it should look like this:
final Function<String,Integer> a = (name)->name.length();
Predicate<String> pred = distinctByKey(a);
System.out.println(pred.test("JOHN"));
System.out.println(pred.test("STEPHEN"));
System.out.println(pred.test("ORTIZ"));
System.out.println(pred.test("RONDON"));
distinctByKey returns a single instance of the Predicate that caches the ConcurrentHashMap. You could achieve almost the same thing if you replace the creation of the Predicate via the lambda with an anonymous inner class for example.
I thought that the distinctByKey method is called or interpreted in
each iteration of the Stream i mean the Map being instance in each
turn but it's not! my question is the body of the Predicate method
call only one time? in the Stream iteration is this a assertion?
No. Streams are not magic, and they do not overthrow standard Java semantics. Consider the code presented:
final long d = Stream.of("JOHN","STEPHEN","ORTIZ","RONDON")
.filter(distinctByKey(Function.identity()))
.count();
Taking specific types and methods out of the picture, that has this general form:
long x = A.b(y).c(z).d(w);
There's no reason to expect that any of a(), b(), or c() is invoked more than once in that chain, and or that their arguments are evaluated more than once each. That is not affected by some of the types being Stream.
What happens instead in your case is that the Predicate returned by (the sole invocation of) your distinctByKey() method is used more than once as the stream in which it is embedded is processed. That Predicate contains a reference to a Map, which it uses and modifies in performing its work.
I've learned that there are 4 kinds of types in method reference. But I don't understand the difference between "Reference to a static method" and "Reference to an instance method of an arbitrary object of a particular type".
For example:
List<String> weeks = new ArrayList<>();
weeks.add("Monday");
weeks.add("Tuesday");
weeks.add("Wednesday");
weeks.add("Thursday");
weeks.add("Friday");
weeks.add("Saturday");
weeks.add("Sunday");
weeks.stream().map(String::toUpperCase).forEach(System.out::println);
The method toUpperCase is not a static method... so why can one write in the way above, rather than needing to use it this way:
weeks.stream().map(s -> s.toUpperCase()).forEach(System.out::println);
Explanation
The method toUpperCase is not a static method... so why can one write in the way above, rather than needing to use it this way:
weeks.stream().map(s -> s.toUpperCase()).forEach(System.out::println);
Method references are not limited to static methods. Take a look at
.map(String::toUpperCase)
it is equivalent to
.map(s -> s.toUpperCase())
Java will just call the method you have referenced on the elements in the stream. In fact, this is the whole point of references.
The official Oracle tutorial explains this in more detail.
Insights, Examples
The method Stream#map (documentation) has the following signature:
<R> Stream<R> map(Function<? super T, ? extends R> mapper)
So it expects some Function. In your case this is a Function<String, String> which takes a String, applies some method on it and then returns a String.
Now we take a look at Function (documentation). It has the following method:
R apply(T t)
Applies this function to the given argument.
This is exactly what you are providing with your method reference. You provide a Function<String, String> that applies the given method reference on all objects. Your apply would look like:
String apply(String t) {
return t.toUpperCase();
}
And the Lambda expression
.map(s -> s.toUpperCase())
generates the exact same Function with the same apply method.
So what you could do is
Function<String, String> toUpper1 = String::toUpperCase;
Function<String, String> toUpper2 = s -> s.toUpperCase();
System.out.println(toUpper1.apply("test"));
System.out.println(toUpper2.apply("test"));
And they will both output "TEST", they behave the same.
More details on this can be found in the Java Language Specification JLS§15.13. Especially take a look at the examples in the end of the chapter.
Another note, why does Java even know that String::toUpperCase should be interpreted as Function<String, String>? Well, in general it does not. That's why we always need to clearly specify the type:
// The left side of the statement makes it clear to the compiler
Function<String, String> toUpper1 = String::toUpperCase;
// The signature of the 'map' method makes it clear to the compiler
.map(String::toUpperCase)
Also note that we can only do such stuff with functional interfaces:
#FunctionalInterface
public interface Function<T, R> { ... }
Note on System.out::println
For some reason you are not confused by
.forEach(System.out::println);
This method is not static either.
The out is an ordinary object instance and the println is a non static method of the PrintStream (documentation) class. See System#out for the objects documentation.
Method reference quite intelligent feature in Java. So, when you use non-static method reference like String:toUpperCase Java automatically comes to know that it needs to call toUpperCase on the on the first parameter.Suppose there is two parameter a lambda expression expect then the method will call on the first parameter and the second parameter will pass as an argument of the method. Let' take an example.
List<String> empNames = Arrays.asList("Tom","Bob");
String s1 = empNames.stream().reduce("",String::concat); //line -1
String s2 = empNames.stream().reduce("",(a,b)->a.concat(b)); // line -2
System.out.println(s1);
System.out.println(s2);
So, on above example on line -1, String#concat method will call on the first parameter (that is a line-2) and a second parameter (that b for line -2) will pass as the argument.
It is possible for the multiple arguments (more than 2) method also but you need to very careful about the which sequence of the parameters.
I highly recommend you to read the Oracle's article about method references: https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html
That is the form of a lambda expression:
s->s.toUpperCase()
And that is a method reference:
String::toUpperCase
Semantically, the method reference is the same as the lambda expression, it just has different syntax.
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.