Java 8 Chained Method Reference

Java 8 chained method reference?

No, method references do not support chaining. In your example it wouldn’t be clear which of the two methods ought to receive the second parameter.


But if you insist on it…

static <V,T,U> BiConsumer<V,U> filterFirstArg(BiConsumer<T,U> c, Function<V,T> f) {
return (t,u)->c.accept(f.apply(t), u);
}

BiConsumer<MyBean, String> c = filterFirstArg(List::add, MyBean::getList);

The naming of the method suggest to view it as taking an existing BiConsumer (here, List.add) and prepend a function (here, MyBean.getList()) to its first argument. It’s easy to imagine how an equivalent utility method for filtering the second argument or both at once may look like.

However, it’s mainly useful for combining existing implementations with another operation. In your specific example, the use site is not better than the ordinary lambda expression

BiConsumer<MyBean, String> c = (myBean, id) -> myBean.getList().add(id);

java8: method reference from another method reference

You need to use two map operations in that case:

roster.stream().map(Person::getSibling).map(Person::getAge).collect(Collectors.toList());

The first one maps the Person to its sibling and the second one maps the Person to its age.

Replacing chained method call using method reference

You need to create a separate method that takes an Artist and returns a boolean:

private boolean nameStartsWithThe(Artist a) {
return a.getName().startsWith("The");
}

Set<Nationality> nationalities = album.getMusician()
.filter(this::nameStartsWithThe)

or with a static method:

private static boolean nameStartsWithThe(Artist a) {
return a.getName().startsWith("The");
}

Set<Nationality> nationalities = album.getMusician()
.filter(MyClass::nameStartsWithThe)

Chain of Map method references

Nope, these are the two ways of doing it. Anything else would end up being only less clear.

But, since you asked, here are some options.

static<T,U,R> Function<T,R> chain(
Function<? super T, ? extends U> f1,
Function<? super U, ? extends R> f2) {
return t -> f2.apply(f1.apply(t));
}

stream.map(chain(Status::getUser, User::getId))

Or

static<T,R> Function<T,R> func(Function<T,R> f) {
return f;
}

stream.map(func(Status::getUser).andThen(User::getId))

How does method chaining work in Java 8 Comparator?

As you chain both, the compiler cannot infer the type argument of the returned comparator of comparing()because it depends on the returned comparator of thenComparingInt() that itself cannot be inferred.

Specify the type in the lambda parameter of comparing() (or use a method reference) and it solves the inference issue as the returned type of comparing() could so be inferred. :

    Comparator<Squirrel> c = Comparator.comparing((Squirrel s)  -> s.getSpecies())
.thenComparingInt(s -> s.getWeight());

Note that specifying the type in the lambda parameter of thenComparingInt() (or using a method reference) such as :

    Comparator<Squirrel> c = Comparator.comparing(s -> s.getSpecies())
.thenComparingInt((Squirrel s) -> s.getWeight());

will not work as the receiver (here the return type of the chained method) is not considered in the inference type computation.

This JDK 8 tutorial/documentation explains that very well :

Note: It is important to note that the inference algorithm uses only
invocation arguments, target types, and possibly an obvious expected
return type to infer types. The inference algorithm does not use
results from later in the program.

Method reference and chained predicate

There is no method in the String class or in the JDK that I know of that is equivalent to your Predicate. You can make a custom one

public static boolean match(String arg) {
return "true".equals(arg) || "false".equals(arg);
}

and use it

assertThat("only true & false strings allowed",
records.stream().map(Record::getType).allMatch(Example::match);

Or you can use a Pattern regular expression as a Predicate

assertThat("only true & false strings allowed",
records.stream().map(Record::getType)
.allMatch(Pattern.compile("^(false|true)$").asPredicate()));

And, as suggested by Holger in comments, you could use

.allMatch(Arrays.asList("true", "false")::contains)

or something similar created from a Set.

How to chain PredicateT in filter() while using method references?

You can't do that because this:

Movie::titleFilter(titleFilter)

isn't really a method reference. You are kind of passing in a parameter, which seems to indicate that you want to call the method. But you are using ::, which makes it look like a method references.

If you just did Movie::titleFilter however, that would be a method reference, but it can't be converted to Predicate<Movie>. It could be converted to Function<String, Predicate<Movie>> if you had specified the type explicitly, but that's obviously not what you want.

Also note that even if you had two methods in Movie like this (these are both convertible to Predicate<Movie>):

public boolean predicate1() { ... }

public boolean predicate2() { ... }

You cannot or them like this:

Movie::predicate1.or(Movie::predicate2)

Because Movie::predicate1 doesn't have a type in and of itself. This is to allow it to be converted to any functional interface with a compatible signature. You have to do:

((Predicate<Movie>)Movie::predicate1).or(Movie::predicate2)

instead.

What I think you intended is:

// note the static modifiers
public static Predicate<Movie> titleFilter(String filter){
return m -> m.getTitle().toLowerCase().contains(filter.toLowerCase());
}

public static Predicate<Movie> translationFilter(String filter){
return m -> m.getTranslation().toLowerCase().contains(filter.toLowerCase());
}

And:

// note that I am calling the methods which *return* a Predicate<Movie>
ArrayList<Movie> filteredMovies = (ArrayList<Movie>) app.getMovies().stream()
.filter((Movie.titleFilter(titleFilter)).or(Movie.translationFilter(translationFilter)))
.collect(Collectors.toList());

Basically, to make a method reference, you can't pass parameters to it, and there's not such thing as "partial application" of a method in Java. (It'd be cool if it existed though...)

Double Colon lambda for two levels method invocation

What you are expecting is called chaining. Method references does not support chaining.

Read more about Method References.



Related Topics



Leave a reply



Submit