1/ Working code:
public Student process (int id, name){
Optional<Student> studentOpt = myrepo.findById(id);
studentOpt.isPresent() {
return updateStudent(id, name);
} else {
return createStudent(id, name);
}
2/ I try to change it to 'full lambdas code' (not working):
public Student process (int id, name){
Optional<Student> studentOpt = myrepo.findById(id);
return studentOpt.ifPresentOrElse(student-> return updateStudent(id, name), () -> return createStudent(id, name));
}
1/ should I change it to full lambda? what is the cleanest?
2/ if yes, how ?
Given that your methods updateStudent and createStudent involve some form of side effect and you should generally prefer side effect free lambdas, I don't recommend you use them here. In fact, a simple if-then-else block would be sufficient. However, if you are curious, the equivalent lambda would look like:
return studentOpt
.map(unused -> updateStudent(id, name))
.orElseGet(() -> createStudent(id, name));
Related
I have the following methods.
#Transactional
public Mono<Long> processPollResult(final PollReport pollReport, final String xmlResult,
final String ref, final String clientId, int orgId) {
return pollOrderRepository.save(toNewOrphanPollOrder(clientId, orgId)).flatMap(pollOder -> {
List<Mono<?>> monoPubs = new ArrayList<>();
monoPubs.add(pollOrderOrphanRepository.save(toPollOrderOrphan(pollOder.getPollOrderId())));
labReport.getLabResults().getLabResultList().forEach(labResult -> {
monoPubs.add((Mono<?>) saveOrphanPollOrderResult(ref, clientId, pollOder, pollResult).subscribe());
});
List<Polls> polls = buildAcknowledgePoll(ref, clientId);
return Mono.when(monoPubs).then(deviceService.acknowledgePolls(polls, clientId)).thenReturn(pollOder.getPollOrderId());
});
}
private Mono<Long> saveOrphanPollOrderResult(final String ref, final String clientId, PollOrder pollOder,
PollResult pollResult) {
return pollOrderTestRepository.save(toPollOrderTest(pollOrder, pollResult.getPollResultHeader()))
.flatMap(pollOrderTestRes -> {
if (pollResult.getPollResultItems() != null
&& pollResult.getPollResultItems().getPollResultItemsList() != null) {
List<PollOrderTestItem> testResultItemsList = labResult.getPollResultItems()
.getPollResultItemsList().stream().map(pollOrderItem -> toPollOrderItem(pollOrderItem,
pollOrder.getPollOrderId(), pollResult.getPollResultHeader().getTestCode()))
.collect(Collectors.toList());
return pollOrderTestItemRepository.saveAll(testResultItemsList).collectList()
.flatMap(pollOrderItemResp -> {
return Mono.just(pollOrder.getPollOrderId());
});
} else {
return Mono.just(pollOrder.getPollOrderId());
}
});
}
Here,
1.
monoPubs.add((Mono<?>) saveOrphanPollOrderResult(ref, clientId, pollOder, pollResult).subscribe()); is troubling me.
Here, getting type cast error.
2.
If I dont put .subscribe(), the statements inside the saveOrphanPollOrderResult is not getting invoked.
Keeping without subscribe here,
monoPubs.add(saveOrphanPollOrderResult(ref, clientId, pollOder, pollResult));
and adding subscribe() inside saveOrphanPollOrderResult like the one below causing compile time error as it cannot return any value and unable to add this inside the monos list.
private Mono<Long> saveOrphanPollOrderResult(final String ref, final String clientId, PollOrder pollOder,
PollResult pollResult) {
return pollOrderTestRepository.save(toPollOrderTest(pollOrder, pollResult.getPollResultHeader()))
.flatMap(pollOrderTestRes -> {
if (pollResult.getPollResultItems() != null
&& pollResult.getPollResultItems().getPollResultItemsList() != null) {
List<PollOrderTestItem> testResultItemsList = labResult.getPollResultItems()
.getPollResultItemsList().stream().map(pollOrderItem -> toPollOrderItem(pollOrderItem,
pollOrder.getPollOrderId(), pollResult.getPollResultHeader().getTestCode()))
.collect(Collectors.toList());
return pollOrderTestItemRepository.saveAll(testResultItemsList).collectList()
.flatMap(pollOrderItemResp -> {
return Mono.just(pollOrder.getPollOrderId());
});
} else {
return Mono.just(pollOrder.getPollOrderId());
}
}).subscribe(); /// -->
}
your problem is here:
var id = pollOder.getPollOrderId();
var pollOrderOrphan = toPollOrderOrphan(id);
List<Mono<?>> monoPubs = new ArrayList<>();
monoPubs.add(pollOrderOrphanRepository.save(pollOrderOrphan));
labReport.getLabResults().getLabResultList().forEach(labResult -> {
monoPubs.add((Mono<?>) saveOrphanPollOrderResult(ref, clientId, pollOder, pollResult).subscribe());
});
There are several issues, first of all using Mono<?> means that you want to store several different types in a List<T> this is bad practice of many different reasons you are probably aware of. Please use the type system.
Second of all is that you can't use a forEach loop. That is imperative programming, not reactive, that assumes that you have the items that you want to place in your list. Which you don't.
That's the reason to why you have to call subscribe, because you need to "wait in the concrete items" so you can place them in your list.
What you need to do is to start using Flux instead, which is a stream of presumed items.
i don't know your exact logic here since your code is quite messy, but as an example:
var labresultList = labReport.getLabResults().getLabResultList();
Mono<List<PollOrderResult> = Flux.fromIterable(labResultList)
.flatMap(saveOrphanPollOrderResult(ref, clientId, pollOder, pollResult))
.collectList();
And some OT:
I dont know what your item types are, but please try to avoid using <?> as that will most likely come back to bite you later.
And please use a bit more functions for readability, as the code is very hard to read, functions are free to use. Assigning variables instead of nesting calls also improves readability.
I have a base class: "Working Items". Witch is extended by 3 subclasses: "Bug", "Story", "Feedback". I receive a command from the console (String), to list all types of Working Items, or one specific type (Bug, Story, Feedback).
I have a collection of type WorkingItems, which contains all of the 3 above mention subclasses. How can I validate while looping the collection if the current object is Bug, Story or Feedback (I can't use instanceof)?
I don't want to divide the collection of Working Items into 3 smaller collections of Bugs, Stories, Feedbacks.
private String listAllWorkingItems(StringBuilder result) {
WIMRepository.getWorkItemList().forEach(workItem -> result.append(workItem.toString()));
return result.toString();
}
private String listAllStories(StringBuilder result) {
WIMRepository.getWorkItemList(); //TODO
return result.toString();
}
private String listAllBugs(StringBuilder result) {
WIMRepository.getWorkItemList() //TODO
return result.toString();
}
private String listAllFeedbacks(StringBuilder result) {
WIMRepository.getWorkItemList() //TODO
return result.toString();
}
The result should be like this:
Command: show all
Result: "printing working items collection"
Command: show bugs
Result: "printing only the bugs from working items collection"
Correct me if I am wrong, but I don't see any point in having 3 subclasses.
enum WorkingItemType {
BUG, STORY, FEEDBACK;
}
class WorkingItem {
private WorkingItemType type;
public WorkingItemType getType() {
return type;
}
}
You didn't show why you've chosen that way, and I am assuming that two classes (WorkingItem and WorkingItemType) would be enough, which would make the method listAllBugs as simple as
WIMRepository.getWorkItemList().stream()
.filter(i -> WorkingItemType.BUG.equals(i.getType()))
.forEach(i -> result.append(i.toString()));
Moreover, one general method would be enough.
WIMRepository.getWorkItemList().stream()
.filter(i -> WorkingItemType.valueOf(input.toUpperCase()).equals(i.getType()))
.forEach(i -> result.append(i.toString()));
instanceof is rarely a good choice, so it's nice that the requirements prohibit it.
Modifying a local variable in forEach gives a compile error:
Normal
int ordinal = 0;
for (Example s : list) {
s.setOrdinal(ordinal);
ordinal++;
}
With Lambda
int ordinal = 0;
list.forEach(s -> {
s.setOrdinal(ordinal);
ordinal++;
});
Any idea how to resolve this?
Use a wrapper
Any kind of wrapper is good.
With Java 10+, use this construct as it's very easy to setup:
var wrapper = new Object(){ int ordinal = 0; };
list.forEach(s -> {
s.setOrdinal(wrapper.ordinal++);
});
With Java 8+, use either an AtomicInteger:
AtomicInteger ordinal = new AtomicInteger(0);
list.forEach(s -> {
s.setOrdinal(ordinal.getAndIncrement());
});
... or an array:
int[] ordinal = { 0 };
list.forEach(s -> {
s.setOrdinal(ordinal[0]++);
});
Note: be very careful if you use a parallel stream. You might not end up with the expected result. Other solutions like Stuart's might be more adapted for those cases.
For types other than int
Of course, this is still valid for types other than int.
For instance, with Java 10+:
var wrapper = new Object(){ String value = ""; };
list.forEach(s->{
wrapper.value += "blah";
});
Or if you're stuck with Java 8 or 9, use the same kind of construct as we did above, but with an AtomicReference...
AtomicReference<String> value = new AtomicReference<>("");
list.forEach(s -> {
value.set(value.get() + s);
});
... or an array:
String[] value = { "" };
list.forEach(s-> {
value[0] += s;
});
This is fairly close to an XY problem. That is, the question being asked is essentially how to mutate a captured local variable from a lambda. But the actual task at hand is how to number the elements of a list.
In my experience, upward of 80% of the time there is a question of how to mutate a captured local from within a lambda, there's a better way to proceed. Usually this involves reduction, but in this case the technique of running a stream over the list indexes applies well:
IntStream.range(0, list.size())
.forEach(i -> list.get(i).setOrdinal(i));
If you only need to pass the value from the outside into the lambda, and not get it out, you can do it with a regular anonymous class instead of a lambda:
list.forEach(new Consumer<Example>() {
int ordinal = 0;
public void accept(Example s) {
s.setOrdinal(ordinal);
ordinal++;
}
});
As the used variables from outside the lamda have to be (implicitly) final, you have to use something like AtomicInteger or write your own data structure.
See
https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html#accessing-local-variables.
An alternative to AtomicInteger is to use an array (or any other object able to store a value):
final int ordinal[] = new int[] { 0 };
list.forEach ( s -> s.setOrdinal ( ordinal[ 0 ]++ ) );
But see the Stuart's answer: there might be a better way to deal with your case.
Yes, you can modify local variables from inside lambdas (in the way shown by the other answers), but you should not do it. Lambdas have been made for functional style of programming and this means: No side effects. What you want to do is considered bad style. It is also dangerous in case of parallel streams.
You should either find a solution without side effects or use a traditional for loop.
If you are on Java 10, you can use var for that:
var ordinal = new Object() { int value; };
list.forEach(s -> {
s.setOrdinal(ordinal.value);
ordinal.value++;
});
You can wrap it up to workaround the compiler but please remember that side effects in lambdas are discouraged.
To quote the javadoc
Side-effects in behavioral parameters to stream operations are, in general, discouraged, as they can often lead to unwitting violations of the statelessness requirement
A small number of stream operations, such as forEach() and peek(), can operate only via side-effects; these should be used with care
I had a slightly different problem. Instead of incrementing a local variable in the forEach, I needed to assign an object to the local variable.
I solved this by defining a private inner domain class that wraps both the list I want to iterate over (countryList) and the output I hope to get from that list (foundCountry). Then using Java 8 "forEach", I iterate over the list field, and when the object I want is found, I assign that object to the output field. So this assigns a value to a field of the local variable, not changing the local variable itself. I believe that since the local variable itself is not changed, the compiler doesn't complain. I can then use the value that I captured in the output field, outside of the list.
Domain Object:
public class Country {
private int id;
private String countryName;
public Country(int id, String countryName){
this.id = id;
this.countryName = countryName;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCountryName() {
return countryName;
}
public void setCountryName(String countryName) {
this.countryName = countryName;
}
}
Wrapper object:
private class CountryFound{
private final List<Country> countryList;
private Country foundCountry;
public CountryFound(List<Country> countryList, Country foundCountry){
this.countryList = countryList;
this.foundCountry = foundCountry;
}
public List<Country> getCountryList() {
return countryList;
}
public void setCountryList(List<Country> countryList) {
this.countryList = countryList;
}
public Country getFoundCountry() {
return foundCountry;
}
public void setFoundCountry(Country foundCountry) {
this.foundCountry = foundCountry;
}
}
Iterate operation:
int id = 5;
CountryFound countryFound = new CountryFound(countryList, null);
countryFound.getCountryList().forEach(c -> {
if(c.getId() == id){
countryFound.setFoundCountry(c);
}
});
System.out.println("Country found: " + countryFound.getFoundCountry().getCountryName());
You could remove the wrapper class method "setCountryList()" and make the field "countryList" final, but I did not get compilation errors leaving these details as-is.
To have a more general solution, you can write a generic Wrapper class:
public static class Wrapper<T> {
public T obj;
public Wrapper(T obj) { this.obj = obj; }
}
...
Wrapper<Integer> w = new Wrapper<>(0);
this.forEach(s -> {
s.setOrdinal(w.obj);
w.obj++;
});
(this is a variant of the solution given by Almir Campos).
In the specific case this is not a good solution, as Integer is worse than int for your purpose, anyway this solution is more general I think.
I have a rather simple question for you guys. In Java 8 it was introduced the Optional type. I have two objects of type Optional<String> and I want to know which is the more elegant way to concatenate them.
Optional<String> first = Optional.ofNullable(/* Some string */);
Optional<String> second = Optional.ofNullable(/* Some other string */);
Optional<String> result = /* Some fancy function that concats first and second */;
In detail, if one of the two original Optional<String> objects was equal to Optional.empty(), I want the whole concatenation to be empty too.
Please, note that I am not asking how to concatenate the evaluation of two Optionals in Java, but how to concatenate two Strings that are inside some Optional.
Thanks in advance.
The solution I found is the following:
first.flatMap(s -> second.map(s1 -> s + s1));
which can be cleaned using a dedicated method, such the following:
first.flatMap(this::concat);
Optional<String> concat(String s) {
second.map(s1 -> s + s1);
}
However, I think that something better can be found.
If we want to generalize to a list or an array of Optional<String>, then we can use something similar to the following.
Optional<String> result =
Stream.of(Optional.of("value1"), Optional.<String>empty())
.reduce(Optional.of(""), this::concat);
// Where the following method id used
Optional<String> concat(Optional<String> first, Optional<String> second) {
return first.flatMap(s -> second.map(s1 -> s + s1));
}
Note that in order to compile the above code, we have to manually bind the type variable of Optional.empty() to String.
You can stream the Optionals and reduce them with a concat.
Optional<String> first = Optional.of("foo");
Optional<String> second = Optional.of("bar");
Optional<String> result = Stream.of(first, second).flatMap(Optional::stream).reduce(String::concat);
If you are using Java 8 replace the flatMap operator with filter(Optional::isPresent).map(Optional::get).
Consider also to use the joining collectors: this will return String, not an Optional<String>.
You can use something like :
Optional<String> result;
result = first.isPresent() && second.isPresent() ? Optional.of(first.get() + second.get()) : Optional.empty();
Any solution that requires a flexible number of optional strings must explicitly use a StringBuilder, rather than rely on the compiler to generate one for you.
String concatThem(Stream<String> stringsin) {
StringBuilder sb = new StringBuilder();
stringsin.forEach(s -> sb.append(s));
return sb.toString();
}
If you have a Stream<Optional<String>> then it becomes:
String concatThem(Stream<Optional<String>> stringsin) {
StringBuilder sb = new StringBuilder();
stringsin.filter(Optional::isPresent).forEach(s -> sb.append(s.get()));
return sb.toString();
}
Otherwise if you have N optional strings you end-up with a heavy cycle of creation and destruction of N-1 single-use StringBuilder objects (generated at compile time) and N-1 strings.
Edit: I had misread, so here's how to do it if any of them is missing to clear it all:
String concatThem(Stream<Optional<String>> stringsin) {
StringBuilder sb = new StringBuilder();
try {
stringsin.forEach(s -> {
if (!s.isPresent()) throw new IllegalArgumentException();
sb.append(s.get())
});
}
catch(IllegalArgumentException ex) {
sb.setLength(0);
}
return sb.toString();
}
This is of course if you insist on using the new API that's light on the syntax and heavy on the execution.
#SafeVarargs
public final Optional<String> concat(Optional<String>... inputs)
{
return Arrays.stream(inputs)
.reduce((left, right) -> left.flatMap(leftValue -> right.map(rightValue -> leftValue + rightValue)))
.get();
}
#Test
public void shouldReturnEmptyIfFirstItemIsEmpty()
{
assertThat(concat(Optional.empty(), Optional.of("B")), is(Optional.empty()));
}
#Test
public void shouldReturnEmptyIfSecondItemIsEmpty()
{
assertThat(concat(Optional.of("A"), Optional.empty()), is(Optional.empty()));
}
#Test
public void shouldConcatIfNoItemIsEmpty()
{
assertThat(concat(Optional.of("A"), Optional.of("B")), is(Optional.of("AB")));
}
Here's an implementation using the reduce method on Stream.
Here's another pretty way:
#Value.Immutable
public abstract class Person {
public Optional<String> firstName() {
return Optional.of("John");
}
public Optional<String> lastName() {
return Optional.of("Smith");
}
public Optional<String> location() {
return Optional.of("Paris");
}
#Value.Lazy
public String concat() {
return Stream.of(firstName(), lastName(), location())
.filter(Optional::isPresent)
.map(Optional::get)
.filter(StringUtils::isNotBlank)
.reduce((first, second) -> first + '.' + second)
.orElse("");
}
}
Note that, as mentioned in other comments, the concat() method performs string concatenations without using a StringBuilder (which might not be performant if you call the method a lot of times). To fix this, in the above example we're using Immutables' [1] #Value.Lazy, which makes sure the concat() method is called once and the result is cached for further calls. Works great!
[1] https://immutables.github.io
I have just begun to use Java 8 and I am wondering if there is a way to write a method that returns a Function?
Right now I have method like below:
Function<Integer, String> getMyFunction() {
return new Function<Integer, String>() {
#Override public String apply(Integer integer) {
return "Hello, world!"
}
}
}
Is there a way to write that more succinctly in Java 8? I was hoping this would work but it does not:
Function<Integer, String> getMyFunction() {
return (it) -> { return "Hello, world: " + it }
}
Get rid of your return statement inside of your function definition:
Function<Integer, String> getMyFunction() {
return (it) -> "Hello, world: " + it;
}
You are missing semi colons:
return (it) -> { return "Hello, world: " + it; };
Although as noted it can be shortened to:
return it -> "Hello, world: " + it;
I would like to point out that it might be more appropriate to use the built-in IntFunction in this case:
IntFunction<String> getMyFunction() {
return it -> "Hello, world: " + it;
}
IntFunction is a part of the standard API for functional interfaces which defines a range of good to have interfaces, mostly related to Java primitives.
You could write it simply like that:
Function<Integer, String> function = n -> "Hello, world " + n;
So, the answer for 99% of the cases has been given by #assylias
You are missing semi colons:
return (it) -> { return "Hello, world: " + it; }; Although as noted it
can be shortened to:
return it -> "Hello, world: " + it;
Yet, I think that it's worth it to add that, if you want to assign your lambda to a variable (to use later).
You can do so by typing:
Callable<YourClass> findIt = () -> returnInstanceOfYourClass();
And then you can easily use it, one example of such a use:
if(dontNeedzToWrap()) {
return findIt.call();
}
return Wrapp.withTransaction(() -> findIt.call());
Given, things can be even made simpler if the Wrapp.withTransaction() method accepts the same kind of Callable's as parameters.
(I use this for JPA atm)