Replacing chained method call using method reference - java

"Java 8 Lambdas: Pragmatic Functional Programming" has an example for using peek method in Stream API. This piece of code prints artist nationalities whose name starts with "The":
Set<Nationality> nationalities = album.getMusician()
.filter(artist -> artist.getName().startsWith("The"))
.map(artist -> artist.getNationality())
.peek(nation -> System.out.println(nation))
.collect(Collectors.toList());
I want to rewrite this code with method references:
Set<Nationality> nationalities = album.getMusician()
.filter(artist -> artist.getName().startsWith("The"))
.map(Artist::getNationality)
.peek(System.out::println)
.collect(Collectors.toList());
Is there any solution to rewrite filter(artist -> artist.getName().startsWith("The"))?

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)

You'd need something that composes the two methods. There are some methods for composing methods (IntUnaryOperator has compose and andThen methods that can compose two IntUnaryOperators into a new IntUnaryOperator). But the ones I've found all seem specialized for certain types of functional interfaces; defining compose methods for every possible pair of functional interface types would be too unwieldy.
I did get something to work that would compose a Function and a Predicate to get a new Predicate:
static <T,U> Predicate<T> functionPredicate(Function<T,U> func, Predicate<U> pred) {
return obj -> pred.test(func.apply(obj));
}
That is, it can compose a predicate that operates on T from a function that takes a T and returns U, and a predicate that operates on U. This would almost work on your example, except that startsWith needs another parameter. But this does work:
static boolean startsWithThe(String s) {
return s.startsWith("The");
}
Predicate<Artist> pred = functionPredicate(Artist::getName, ThisClass::startsWithThe);
where ThisClass is whatever class contains startsWithThe. This works. If you want to avoid writing a new method (like startsWithThe), you could probably write a "parameterized predicate" generic method so that you write something like
Predicate<Artist> pred = functionPredicate(Artist::getName, parameterizedPredicate(String::startsWith, "The"));
but I haven't tried it.
So it seems it's possible to come up with something that will let you use method references instead of lambdas. I question whether it's worthwhile. To me, a method reference is just a shorthand for certain kinds of lambdas; and unless you can do what you want with a simple method reference, I'd think using a lambda is concise and clear enough, and you don't need to add all the extra rigmarole like my functionPredicate method. I've seen several questions that ask something like "How can I use a method reference for this instead of a lambda?", and I honestly don't understand why.

There is no way to replace that line with a method reference.
Method reference works by using the fact that there is only one object being used in entire lambda expression and the compiler can infer it (reference does not matter and type can be inferred) using target typing.
So,
artist -> artist.getNationality()
is replaced with
Artist::getNationality
Here Artist::getNationality method matches with the target type without requiring any further information.
In case of artist -> artist.getName().startsWith("The"), there are two method calls in the lambda expression. The order, parameters are important, and have to be specified.
It looks as if the artist reference should be inferred, but the compiler won't know what object should the startsWith("The") method be called on.
Hope this helps.

Related

Can I explicitly type an ambiguous method call and use it?

I'm sure this is answered somewhere, but I don't have good enough search terms to find it. I am using the io.vavr.control.Try package, and when I try to use getOrElseThrow method on an element in a stream of results, the method is ambiguous with the io.vavr.Value class. Can I specify which method I want to use somehow, or is it impossible?
You have a number of options:
Add an explicit cast to the desired type:
.map(rsp -> rsp.getOrElseThrow((Supplier<NotFoundException>) NotFoundException::new))
.map(rsp -> rsp.getOrElseThrow((Function<? super Throwable, NotFoundException>) NotFoundException::new))
Use a lambda expression instead of a method reference:
.map(rsp -> rsp.getOrElseThrow(() -> new NotFoundException()))
.map(rsp -> rsp.getOrElseThrow(t -> new NotFoundException(t)))
Use an explicit type of the outer lambda parameter:
.map((Value<…> rsp) -> rsp.getOrElseThrow(NotFoundException::new))
.map((Try<…> rsp) -> rsp.getOrElseThrow(NotFoundException::new))
You could replace NotFoundException::new with t -> new NotFoundException(t) which will only match the Function argument.
Since you didn't post complete code I can only make an educated guess about how your NotFoundException looks like, but I think it contains at least two constructors in the following form:
public NotFoundException() {}
public NotFoundException(Throwable cause) {
super(cause);
}
If you want to use constructor method references with Try.getOrElseThrow, you'll need to eliminate the method reference ambiguity by removing one of these constructors (or potentially reducing visibility), or fall back to using lambdas for constructing the resulting throwable.
If you cannot, or do not want to change the NotFoundException class, you can either fall back to using a lambda instead of a method reference (1 and 2), or you can create explicit Function(2) or Consumer(3) instances with the help of the vavr function type factory methods:
rsp.getOrElseThrow(cause -> new NotFoundException(cause)); // (1)
rsp.getOrElseThrow(() -> new NotFoundException()); // (2)
rsp.getOrElseThrow(Function1.of(NotFoundException::new)); // (3)
rsp.getOrElseThrow(Function0.of(NotFoundException::new)); // (4)

ModelMapping a Stream without a Lambda Expression

I'm not Java developer, but I have many years of experience in C#. I have a List<Foo> that I need to convert to a List<Bar> using ModelMapper where Foo and Bar have essentially identical properties.
Currently I've written this as:
#AutoWired ModelMapper modelMapper;
...
List<Bar> results = repository
.getFoos()
.stream()
.map(x -> modelMapper.map(x, Bar.class))
.collect(Collectors.toList());
This works fine. However, I feel like that lambda expression could be replaced with just a simple method reference. If this were C#, I'd probably be able to do something along the lines of this:
var results = repository.getFoos().Select(modelMapper.map<Bar>).ToList();
But I can't find the right syntax in Java. I've tried this:
.map(modelMapper::<Bar>map)
But I get the error:
Cannot resolve method 'map'
I am not sure if this is because I've mixed up the syntax somehow, or this is because the map method has a too many overloads to create an unambiguous reference. In case it helps, the overload of map that I'm trying to use is defined as:
public <D> D map(Object source, Class<D> destinationType)
Is there any way to achieve this mapping without a lambda expression?
I am not sure if this is because I've mixed up the syntax somehow, or this is because the map method has a too many overloads to create an unambiguous reference.
You've not messed up the syntax; there's no equivalent in Java (it's often more verbose than C#, and streams are no exception.)
You can only use the method reference (double colon) syntax when the parameters you want to pass to the method are the same, and in the same order, as the parameters in the functional interface.
In the case of map, there's only one parameter, and you need to pass a second parameter (that being Bar.class) to modelMapper.map(), so no method reference syntax is allowed. The only way you could use it were if you were to subclass the modelMapper to work with Bar only, and therefore remove the need for the second explicit class parameter.
I'm pretty confident that the method you're using there is the most concise way of doing things in Java.
Java doesn't support partial function application (related to currying), so you can only do that with a lambda.
Of course, since you don't reference any local variables or parameters, you can define a method which you can then reference.
#AutoWired ModelMapper modelMapper;
private Bar mapToBar(Foo x) {
return modelMapper.map(x, Bar.class);
}
...
List<Bar> results = repository
.getFoos()
.stream()
.map(this::mapToBar)
.collect(Collectors.toList());
That is actually very close to how the lambda is compiled, except that mapToBar for the lambda would be a hidden (synthetic) method.
Actually this can not be done, because your modelMapper.map() method requires two parameters. If you really want to use method reference at this point you could create a new method in your modelMapper, which requires only one parameter or wrap the modelMapper.map() call in another method like this:
private Bar mapFooToBar(Foo x) {
return modelMapper.map(x, Bar.class);
}
You now can use this method with method reference:
List<Bar> results = repository.getFoos().stream()
.map(this::mapFooToBar)
.collect(Collectors.toList());
But at the end I don't think that this will make your code better in any way, so I would recommend using the lambda expression you already have.

Java: Function composition on stream

I need to create some code like this:
this.getPendingDocuments()
.forEach((this::documentProcessed).andThen(this::createAuditory));
So I mean, I need to apply two functions to the same element.
Any ideas?
EDIT
Compiler tells me:
[Java] The target type of this expression must be a functional interface
You can write a lambda expression that executes both methods:
this.getPendingDocuments()
.forEach(doc -> {
this.documentProcessed(doc);
this.createAuditory(doc);
});
When you use forEach() you can add more functions inside... like this
this.getPendingDocuments()
.forEach( element -> {
this.documentProcessed(element);
this.createAuditory(element);
});
You must target your method references to a functional interface type. In this case, forEach requires a Consumer instance:
Consumer<Document> processDocument = this::documentProcessed;
this.getPendingDocuments().forEach(processDocument.andThen(this::createAuditory));
The code above uses Consumer.andThen to chain consumers. Of course, both methods should accept a Document instance as an argument (or whatever the type of the elements of the Iterable returned by the getPendingDocuments() method is).

Java 8 method references and overridden methods

I've been using lambdas and method references in Java 8 for a while and there is this one thing I do not understand. Here is the example code:
Set<Integer> first = Collections.singleton(1);
Set<Integer> second = Collections.singleton(2);
Set<Integer> third = Collections.singleton(3);
Stream.of(first, second, third)
.flatMap(Collection::stream)
.map(String::valueOf)
.forEach(System.out::println);
Stream.of(first, second, third)
.flatMap(Set::stream)
.map(String::valueOf)
.forEach(System.out::println);
The two stream pipelines do the same thing, they print out the three numbers, one per line. The difference is in their second line, it seems you can simply replace the class name in the inheritance hierarchy as long as it has the method (the Collection interface has the default method "stream", which is not redefined in the Set interface).
I tried out what happens if the method is redefined again and again, using these classes:
private static class CustomHashSet<E> extends HashSet<E> {
#Override
public Stream<E> stream() {
System.out.println("Changed method!");
return StreamSupport.stream(spliterator(), false);
}
}
private static class CustomCustomHashSet<E> extends CustomHashSet<E> {
#Override
public Stream<E> stream() {
System.out.println("Changed method again!");
return StreamSupport.stream(spliterator(), false);
}
}
After changing the first, second and third assignments to use these classes I could replace the method references (CustomCustomHashSet::stream) and not surprisingly they did print out the debugging messages in all cases, even when I used Collection::stream. It seems you cannot call the super, overriden method with method references.
Is there any runtime difference? What is the better practice, refer to the top level interface/class or use the concrete, known type (Set)?
Thanks!
Edit:
Just to be clear, I know about inheritance and LSP, my confusion is related to the design of the method references in Java 8. My first thought was that changing the class in a method reference would change the behavior, that it would invoke the super method from the chosen class, but as the tests showed, it makes no difference. Changing the created instance types does change the behavior.
Even method references have to respect to OOP principle of method overriding. Otherwise, code like
public static List<String> stringify(List<?> o) {
return o.stream().map(Object::toString).collect(Collectors.toList());
}
would not work as expected.
As to which class name to use for the method reference: I prefer to use the most general class or interface that declares the method.
The reason is this: you write your method to process a collection of Set. Later on you see that your method might also be useful for a collection of Collection, so you change your method signature accordingly. Now if your code within the method always references Set method, you will have to adjust these method references too:
From
public static <T> void test(Collection<Set<T>> data) {
data.stream().flatMap(Set::stream).forEach(e -> System.out.println(e));
}
to
public static <T> void test(Collection<Collection<T>> data) {
data.stream().flatMap(Collection::stream).forEach(e -> System.out.println(e));
}
you need to change the method body too, whereas if you had written your method as
public static <T> void test(Collection<Set<T>> data) {
data.stream().flatMap(Collection::stream).forEach(e -> System.out.println(e));
}
you will not have to change the method body.
A Set is a Collection. Collection has a stream() method, so Set has that same method too, as do all Set implementations (eg HashSet, TreeSet, etc).
Identifying the method as belonging to any particular supertype makes no difference, as it will always resolve to the actual method declared by the implementation of the object at runtime.
See the Liskov Substitution Principle:
if S is a subtype of T, then objects of type T may be replaced with objects of type S without altering any of the desirable properties of that program

Are reverse method references possible in Java 8?

I know about lambda method references.
However, I am wondering whether the reverse might be possible, because I have a method that just proxies its arguments to a lambda:
Function<Arg, Result> lambda = (a) -> new Result(a);
public Result myMethod(Arg arg) {
return lambda.apply(a);
}
Haven't found anything on Google, so I guess it's not possible. Which makes sense, because after all, as I understand it, a lambda is just shorthand for a whole interface. A method and an interface are different. But you can make a lambda from a method, so maybe you can make a method from a lambda?
You can't make a method from a lambda because, as you say, a lambda is not a method and more importantly you cannot dynamically change a class by adding methods to it at runtime. That's a basic design invariant of Java classes. It is possible to dynamically respond to a predefined method of an interface with your own implementation, although it's fairly clunky. Take a look at http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Proxy.html
The variable lambda has the type Function which doesn’t say anything about how the instance has been created. It might be a lambda expression, but it doesn’t have to. That said, if you want to delegate myMethod to a method declared in Function, there is no reason to automatically choose the abstract method of that interface, so, similar to method references, you would have to specify the target method like lambda::apply to make clear you want that method and not one of the other methods of the interface Function.
But unlike method references, which use a target type, you can’t derive a method declaration from the surrounding context, so you can’t spare the method declaration. So such a hypothetical feature would still require the method declaration, the reference to the lambda field and the target method name (apply), so there is not much left that you can save that would justify a new language feature.
And there is no need for such a functionality anyway. If you have code to be expressed as both, a function and a method, express it as method:
Instead of
Function<Arg, Result> lambda = (a) -> new Result(a);
public Result myMethod(Arg arg) {
return lambda.apply(a);
}
write
Function<Arg, Result> lambda = this::myMethod;
public Result myMethod(Arg arg) {
return new Result(arg);
}
But even a code replication might be acceptable, as in
Function<Arg, Result> lambda = (a) -> new Result(a);
public Result myMethod(Arg arg) {
return new Result(arg);
}
considering that lambda expressions should host rather small, often trivial, code only.

Categories