I wish to do lazy evaluation on a list of functions I've defined as follows;
Optional<Output> output = Stream.<Function<Input, Optional<Output>>> of(
classA::eval, classB::eval, classC::eval)
.map(f -> f.apply(input))
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst();
where as you see, each class (a, b & c) has an Optional<Output> eval(Input in) method defined. If I try to do
Stream.of(...)....
ignoring explicit type, it gives
T is not a functional interface
compilation error. Not accepting functional interface type for T generic type in .of(T... values)
Is there a snappier way of creating a stream of these functions? I hate to explicitly define of method with Function and its in-out types. Wouldn't it work in a more generic manner?
This issue stems from the topic of the following question;
Lambda Expression and generic method
You can break it into two lines:
Stream<Function<Input, Optional<Output>>> stream = Stream
.of(classA::eval, classB::eval, classC::eval);
Optional<Output> out = stream.map(f -> f.apply(input))
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst();
or use casting:
Optional<Output> out = Stream.of(
(<Function<Input, Optional<Output>>>)classA::eval,
classB::eval,
classC::eval)
.map(f -> f.apply(input))
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst();
but I don't think you can avoid specifying the type of the Stream element - Function<Input, Optional<Output>> - somewhere, since otherwise the compiler can't infer it from the method references.
There is a way that allows to omit the Function<Input, Optional<Output>> type, but it’s not necessarily an improvement
Optional<Output> o =
Stream.concat(Stream.of(input).map(classA::eval),
Stream.concat(Stream.of(input).map(classB::eval),
Stream.of(input).map(classC::eval)))
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst();
and it doesn’t scale.
It seems, the best option is to wait for Java-9 where you can use
Optional<Output> o = classA.eval(input)
.or(() -> classB.eval(input))
.or(() -> classC.eval(input));
Related
I want to map a list of tasks to List<Callable<Tasks>>:
List<Callable<Tasks>> callableTasks = tasks.stream().map(t ->
{
// how to return callable directly and omitting curly braces?
Callable<Task> task = () -> run(t);
return task;
}).collect(Collectors.toList());
How can I shorten the above expression to return the callable directly?
You can use static methods from Executors class, for example Executors.callable:
tasks.stream().map(t -> callable(() -> run(t))).collect(Collectors.toList());
The problem is that the lambda expression () -> run(t) requires a target type.
In a construct like
List<Callable<Tasks>> callableTasks = tasks.stream()
.map(…).collect(Collectors.toList());
The assignment to List<Callable<Tasks>> callableTasks provides a target type to the collect method invocation, but can’t propagate it to the preceding map invocation (a general limitation of Java’s current type inference).
By splitting the mapping function into an assignment and a return statement, you are providing a target type. Likewise, you could provide target type by casting, e.g. .map(t -> (Callable<Task>)() -> run(t)) or by providing an explicit type for the generic method map, e.g. .<Callable<Task>>map(t -> () -> run(t)).
The latter solution leads to
List<Callable<Task>> callableTasks = tasks.stream()
.<Callable<Task>>map(t -> () -> run(t)).collect(Collectors.toList());
If the Task instance returned by run(t) is the same as passed to it as argument, you can use Executors.callable like:
List<Callable<Task>> callableTasks = tasks.stream()
.map(t -> Executors.callable(() -> run(t), t)).collect(Collectors.toList());
Note the Executors.callable encapsulates a Runnable, which provides no return value. So the constructed Callable will evaluate to the result specified to Executors.callable as second argument or to null if you use the one argument version.
I am new in Java8 and I want to refactor this piece of code and convert it in a more Java8 style,
for (RestaurantAddressee RestaurantAddressee : consultationRestaurant.getAddressees()) {
Chain chain = chainRestService.getClient().getChainDetails(getTDKUser(), RestaurantAddressee.getChain().getId());
if (chain.getOrganisation().getId().equalsIgnoreCase(event.getOrganisationId())) {
chainIds.add(restaurantAddressee.getChain().getId());
}
}
so I change it for this code:
consultationRestaurant.getAddressees()
.stream()
.map( ma -> chainRestService.getClient().getChainDetails(getTDKUser(), ma.getChain().getId()))
.filter(chain -> chain.getOrganisation().getId().equalsIgnoreCase(event.getOrganisationId()))
.forEach(chainIds.add(chain.getId()));
But I have this compilation error:
chain cannot be resolved
You forgot to specify the lambda expression parameter in your forEach call.
That said, you shouldn't use forEach to add elements to a collection. Use collect:
List<String> chainIds =
consultationRestaurant.getAddressees()
.stream()
.map( ma -> chainRestService.getClient().getChainDetails(getTDKUser(), ma.getChain().getId()))
.filter(chain -> chain.getOrganisation().getId().equalsIgnoreCase(event.getOrganisationId()))
.map(Chain::getId)
.collect(Collectors.toList());
Here. Your loop defines:
Chain chain = chainRestService.getClient()...
But your stream statement simply misses to define that variable.
So: in places that need that variable, you have to provide, for example as parameter:
filter(chain -> chain.getOrganisation().getId().equalsIgnoreCase(event.getOrganisationId()))
I have a requirement to validate a field against some predefined values (that can grow in future). So for this I have created a Enum and defined a method that returns the stream of the allowed values.
public enum EnumDemo {
VERSION("1.0.0","2.0.3");
private List<String> ver;
EnumDemo(String... ver) {
this.ver = Arrays.asList(ver);
}
public List<String> getVer() {
return ver;
}
public static Stream<EnumDemo> stream() {
return Arrays.stream(EnumDemo.values());
}
}
Now I need to validate a field against the values defined in this Enum.
I'm using:
Optional<EnumDemo> ab = EnumDemo.stream()
.map(l -> {l.getVer().stream()
.filter(c -> c.equals("2.0.3"))
.findFirst();})
.findFirst();
System.out.println(ab.get().getVer());
But it is giving me compilation error. Any help would be appreciated.
Edit:
Compilation Error:
The method map(Function<? super EnumDemo,? extends R>) in the type Stream<EnumDemo> is not applicable for the arguments ((<no type> l) -> {})
You should write it this way:
Optional<EnumDemo> ab = EnumDemo.stream().filter(l -> l.getVer().contains("2.0.3"))
.findFirst();
By the way, it wasn't working because you used {} for the lambda expression, so it was expecting a return statement in the {}. You could either remove the {} (along with the ;) or add in the return.
Anyway the original codes looked confusing, not sure if I guessed the intention correctly, but this implementation should be clearer.
Edit
Based on your comment, this is what you need:
EnumDemo.stream().flatMap(l -> l.getVer().stream())
.filter("2.0.3"::equals)
.findAny()
.ifPresent(System.out::println);
Update
Holger commented that there is a shorter and more meaningful way, with better performance:
if(EnumDemo.stream()
.anyMatch(l -> l.getVer().contains(userString))) {
System.out.println(userString);
}
To understand it, you have to think about lambdas. Lambdas represent interfaces but are specially treated by the JVM, so not every Lambda needs a class to represent. (Stateless lambdas can be just methods).
Now when looking at the map() method in the Stream interface:
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
You see that it expects an implementation of the Function interface. You now have many different ways to provide that mapper. In this example lets map from Object to String:
1. Using an inline lambda:
.map(o -> o.toString())
2. Using a multiline lambda:
.map(o -> {
return o.toString();
})
3. Using method references:
.map(Object::toString)
4. Using an anonymous class:
.map(new Function<Object, String>(){
#Override
public String apply(Object o){
return o.toString();
}
})
Your current code uses the 2. approach. But without a return statement. This is even better seen when looking at the anonymous class at 4.. It seems natural, that when not using a return statement in a method that no value is returned.
And that's why you get the compilation error.
You just have to add the return statement:
.map(l -> {
return l.getVer().stream()
.filter(c -> c.equals("2.0.3"))
.findFirst();
});
Or remove the brackets {}:
.map(l -> l.getVer().stream()
.filter(c -> c.equals("2.0.3"))
.findFirst());
Or even use the approach provided by #Jai in his answer. Which works even better, than what you currently have.
You are using lambda expression and not returning any value so it is giving compilation error. It is better to use ifPresent()
String val="2.0.3";
EnumDemo.stream()
.flatMap(l -> l.getVer().stream())
.filter(c -> c.equals(val))
.findAny()
.ifPresent(x -> System.out.println(x));
Can I convert the following code using method reference?
List<Text> childrenToRemove = new ArrayList<>();
group.getChildren().stream()
.filter(c -> c instanceof Text)
.forEach(c -> childrenToRemove.add((Text)c));
Let me give an example to illustrate what I mean, suppose we have
myList
.stream()
.filter(s -> s.startsWith("c"))
.map(String::toUpperCase)
.sorted()
.forEach(elem -> System.out.println(elem));
using method reference it can be written as (last line)
myList
.stream()
.filter(s -> s.startsWith("c"))
.map(String::toUpperCase)
.sorted()
.forEach(System.out::println);
What are the rules to convert an expression to a method reference?
Yes, you can use these method references:
.filter(Text.class::isInstance)
.map(Text.class::cast)
.forEach(childrenToRemove::add);
Instead of for-each-add, you can collect stream items with Collectors.toSet():
Set<Text> childrenToRemove = group.getChildren()
// ...
.collect(Collectors.toSet());
Use toList() if you need to maintain the order of children.
You can replace lambda expressions with method references if the signatures match by applying these rules:
ContainingClass::staticMethodName // method reference to a static method
containingObject::instanceMethodName // method reference to an instance method
ContainingType::methodName // method reference to an instance method
ClassName::new // method reference to a constructor
I think yes it's possible, like so
group.getChildren()
.filter(Text.class::isInstance)
.map(Text.class::cast)
.collect(Collectors.toCollection(() -> childrenToRemove));
I have written following code:
Set<Pair<Predicate>> interestingPairs = ...
...
interestingPairs.stream()
.filter(pair -> pair.getFirst().isNegated() == pair.getSecond().isNegated())
.flatMap( pair -> findUnification(pair)
.map(u -> {
Set<String> aVars = pair.getFirst().getAllVariables();
Set<String> bVars = pair.getSecond().getAllVariables();
if(u.sigma.isEmptyOrVarByVar(aVars) && !u.sigmaPrime.isEmptyOrVarByVar(bVars))
return Stream.of(pair.getFirst());
if (!u.sigma.isEmptyOrVarByVar(bVars) && u.sigmaPrime.isEmptyOrVarByVar(aVars))
return Stream.of(pair.getSecond());
return Stream.empty();
})
.orElse(Stream.empty()))
.forEach(toRemove -> this.predicates.remove((Predicate)toRemove));
I'm using intelliJ IDE. After flatMap operation elements of my stream have Object type instead of Predicate, so in the foreach toRemove has not desired type. When I change return Stream.empty() to return Stream.<Predicate>empty(), object's type after flatMap is Predicate. I would even understand this if not .orElse(Stream.empty()) where I don't have to add the <Predicate implicitly. What's the thing I don't get here?
I assume that your Predicate is your custom non-generic type, not the java.util.function.Predicate. Seems that the compiler fails to infer the type of the .flatMap argument. You may help it specifying it explicitly:
interestingPairs.stream()
.filter(pair -> pair.getFirst().isNegated() == pair.getSecond().isNegated())
.<Predicate>flatMap( pair -> findUnification(pair)
... and so on
To my understanding, current JLS automatic type inference rules do not cover this case, so it's not a compiler bug. You just have to specify type argument explicitly sometimes or extract some complex lambdas to intermediate variables.