How to create reusable optional mapping with method references? - java

While I'm trying to use Optional features with method references, it really confused me how to optimize it with reusable code. I think I'm stuck while trying to use all those new features (for me) at the same time i decided to get rid of java-6 style, now I think i can't think simple, i feel that it gets overcomplicated. How can i create
List<BooleanExpression> expressionMapping = new ArrayList<>();
if (request != null) { // request is input parameter, a DTO
Optional.ofNullable(request.getPlantId())
.map(campaign.plant.id::contains) // campaign is static created by Querydsl
.ifPresent(expressionMapping::add);
Optional.ofNullable(request.getTitle())
.map(campaign.title::containsIgnoreCase)
.ifPresent(expressionMapping::add);
Optional.ofNullable(request.getCampaignNumber())
.map(this::getLikeWrapped)
.map(campaign.campaignNumber::like)
.ifPresent(expressionMapping::add);
... 20 more Optional bunch of code like this
}
also having trouble with writing this code with Optional like previous ones:
if (request.getLockVehicle() != null) {
if (request.getLockVehicle()) {
expressionMapping.add(campaign.lockVehicle.isNotNull());
} else {
expressionMapping.add(campaign.lockVehicle.isNull());
}
}

What about use enum to declare all fields from Request and use it as common part of the code. I did not check it, this is only to show my approach:
public enum RequestField {
PLANT_ID(Request::getPlantId, (val, campaign) -> campaign.plant.id::contains),
TITLE(Request::getTitle, (val, campaign) -> campaign.title::containsIgnoreCase),
CAMPAIGN_NUMBER(Request::getCampaignNumber, (val, campaign) -> campaign.campaignNumber::like),
// ... more fields here ...
;
private final Function<Request, Optional<Object>> get;
private final BiFunction<Object, Campaign, BooleanExpression> map;
RequestField(Function<Request, Object> get, BiFunction<Object, Campaign, BooleanExpression> map) {
this.get = get.andThen(Optional::ofNullable);
this.map = map;
}
public static List<BooleanExpression> getBooleanExpressions(Request request, Campaign campaign) {
if (request == null)
return Collections.emptyList();
List<BooleanExpression> res = new LinkedList<>();
for (RequestField field : values())
field.get.apply(request)
.map(r -> field.map.apply(r, campaign))
.ifPresent(res::add);
return res.isEmpty() ? Collections.emptyList() : Collections.unmodifiableList(res);
}
}
And your client code will be looking like:
List<BooleanExpression> booleanExpressions = RequestField.getBooleanExpressions(request, campaign);
P.S.
Your last code could be look like:
if (request.getLockVehicle() != null)
expressionMapping.add(request.getLockVehicle() ? campaign.lockVehicle.isNotNull() : campaign.lockVehicle.isNull());

The aim of using Optional is informing who is calling that method / parameter that it could be null.
In the first part of your code, you are not getting any advantage from this, you are just rewriting some code wrapping it around Optional logic but, as you said, without any "reusable" purpose.
A useful way is using it as returning value of a method: for example, if you know that your title could be null, you can refactor your getter like
public Optional<String> getTitle(){
return Optional.ofNullable(this.title); //I'm guessing the 'title' variable here
}
This will help you: every time you call getTitle() , you will know that could be null, because you are obtaining an Optional<String> instead of a String.
This will bring then you to:
request.getTitle().ifPresent(title-> title.doSomething())
// you can also add something like .orElse("anotherStringValue")
The second example could be reworked as the first one, making the return of getLockVehicle() as Optional<Boolean>, even if I suggest here setting that with a default value in your class, probably to false... Optional<Boolean> is pretty senseless imho
Hope this helps clearing your mind

Related

How to optimize the if-else statement due to Optional Object's properties also being optional?

I have an Optional Object named Form. This has 3 other optional properties to it. Reason being, if this form object is not present, I have to generate my own form data. If the form is present, users can still choose to leave their properties empty, requiring my method to generate them in that case. The way that I have implemented this logic goes as follows.
private FormSection buildForm(final Optional<Form> formOptional) {
Set<String> formNames = null;
Set<Question> formTypes = null;
Set<Question> formDetails = null;
if (formOptional.isPresent()) {
Form form = formOptional.get();
formNames = formSection.names()
.orElseGet(() -> getNamesFromDB());
formTypes = formSection.formTypes()
.orElseGet(() -> getFormTypesFromDB());
formDetails = formSection.formDetails()
.orElseGet(() -> getFormDetailsFromDB());
} else {
formNames = getNamesFromDB();
formTypes = getFormTypesFromDB();
formDetails = getFormDetailsFromDB();
}
return Form()
.names(formNames)
.formTypes(formTypes)
.formDetails(formDetails)
.build();
}
I feel like I'm redoing some checks and that this whole if-else statement could be optimized. I'm using Java 8 by the way.
Make a form impl that is entirely blank. Then strongly reconsider your APIs; Optional is an appropriate thing to return when you're writing stream terminals. It's borderline okay to return them for methods (but not idiomatic). It's almost universally considered 'wrong' to use them anywhere else (such as fields, or as a parameter type); see for example this top answer (Brian Goetz was team lead for Lambdas at oracle) that tells you not to do this.
The preferred solution to avoid nulls is to have sensible defaults if possible. Not all object kinds have these (and don't force it), but given that you have a form that has 100% optional data, it is appropriate here: Is there a semantic difference between a 'null' form (or a non-present optional, if you prefer), and a present/non-null form where every single thing the form contains is null/non-present?
I don't think you intend for there to be a difference. In which case - great. Ditch optional forms. Adopt the default form. You can then just pass the 'default form' (the one where all 'fields' are non-present) to this method:
class Form {
public static final Form BLANK_FORM = ....;
}
private FormSection buildForm(Form form) {
}
and adjust whatever code currently ends up with an Optional<Form> to instead shift to returning a blank form. For example, instead of:
Optional<Form> formOptional = Optional.ofNullable(x);
use:
Form form = x == null ? Form.BLANK_FORM : x;
Or if you have:
if (....) {
return Optional.empty();
} else {
// fetch all sorts of details.
Form f = new Form(...);
return Optional.of(f);
}
replace those return statements with respectively return Form.BLANK_FORM; and return f;. You get the idea :)
You can use flatMap method from Optional to "chain" operations that return another Optional
formNames = form.flatMap(Form::names).orElseGet(() -> getNamesFromDB());
flatMap gets applied only if form is not empty, otherwise returns an empty optional. If it's present, is replaced by the optional returned by names() method in Form.
private FormSection buildForm(final Optional<Form> opForm) {
Set<String> formNames = opForm.flatMap(Form::names).orElseGet(() -> getNamesFromDB());
Set<Question> formTypes = opForm.flatMap(Form::formTypes).orElseGet(() -> getFormTypesFromDB());
Set<Question> formDetails = opForm.flatMap(Form::formDetails).orElseGet(() -> getFormDetailsFromDB());
return Form()
.names(formNames)
.formTypes(formTypes)
.formDetails(formDetails)
.build();
}

How to use Java 8 Optionals, performing an action if all three are present?

I have some (simplified) code that uses Java Optionals:
Optional<User> maybeTarget = userRepository.findById(id1);
Optional<String> maybeSourceName = userRepository.findById(id2).map(User::getName);
Optional<String> maybeEventName = eventRepository.findById(id3).map(Event::getName);
maybeTarget.ifPresent(target -> {
maybeSourceName.ifPresent(sourceName -> {
maybeEventName.ifPresent(eventName -> {
sendInvite(target.getEmail(), String.format("Hi %s, $s has invited you to $s", target.getName(), sourceName, meetingName));
}
}
}
Needless to say, this looks and feels bad. But I can't think of another way to do this in a less-nested and more readable way. I considered streaming the 3 Optionals, but discarded the idea as doing a .filter(Optional::isPresent) then a .map(Optional::get) feels even worse.
So is there a better, more 'Java 8' or 'Optional-literate' way of dealing with this situation (essentially multiple Optionals all needed to compute a final operation)?
I think to stream the three Optionals is an overkill, why not the simple
if (maybeTarget.isPresent() && maybeSourceName.isPresent() && maybeEventName.isPresent()) {
...
}
In my eyes, this states the conditional logic more clearly compared to the use of the stream API.
Using a helper function, things at least become un-nested a little:
#FunctionalInterface
interface TriConsumer<T, U, S> {
void accept(T t, U u, S s);
}
public static <T, U, S> void allOf(Optional<T> o1, Optional<U> o2, Optional<S> o3,
TriConsumer<T, U, S> consumer) {
o1.ifPresent(t -> o2.ifPresent(u -> o3.ifPresent(s -> consumer.accept(t, u, s))));
}
allOf(maybeTarget, maybeSourceName, maybeEventName,
(target, sourceName, eventName) -> {
/// ...
});
The obvious downside being that you'd need a separate helper function overload for every different number of Optionals
How about something like this
if(Stream.of(maybeTarget, maybeSourceName,
maybeEventName).allMatch(Optional::isPresent))
{
sendinvite(....)// do get on all optionals.
}
Having said that. If your logic to find in database is only to send mail, then if maybeTarget.ifPresent() is false, then there is no point to fetch the other two values, ain't it?. I am afraid, this kinda logic can be achieved only through traditional if else statements.
Since the original code is being executed for its side effects (sending an email), and not extracting or generating a value, the nested ifPresent calls seem appropriate. The original code doesn't seem too bad, and indeed it seems rather better than some of the answers that have been proposed. However, the statement lambdas and the local variables of type Optional do seem to add a fair amount of clutter.
First, I'll take the liberty of modifying the original code by wrapping it in a method, giving the parameters nice names, and making up some type names. I have no idea if the actual code is like this, but this shouldn't really be surprising to anyone.
// original version, slightly modified
void inviteById(UserId targetId, UserId sourceId, EventId eventId) {
Optional<User> maybeTarget = userRepository.findById(targetId);
Optional<String> maybeSourceName = userRepository.findById(sourceId).map(User::getName);
Optional<String> maybeEventName = eventRepository.findById(eventId).map(Event::getName);
maybeTarget.ifPresent(target -> {
maybeSourceName.ifPresent(sourceName -> {
maybeEventName.ifPresent(eventName -> {
sendInvite(target.getEmail(), String.format("Hi %s, %s has invited you to %s",
target.getName(), sourceName, eventName));
});
});
});
}
I played around with different refactorings, and I found that extracting the inner statement lambda into its own method makes the most sense to me. Given source and target users and an event -- no Optional stuff -- it sends mail about it. This is the computation that needs to be performed after all the optional stuff has been dealt with. I've also moved the data extraction (email, name) in here instead of mixing it with the Optional processing in the outer layer. Again, this makes sense to me: send mail from source to target about event.
void setupInvite(User target, User source, Event event) {
sendInvite(target.getEmail(), String.format("Hi %s, %s has invited you to %s",
target.getName(), source.getName(), event.getName()));
}
Now, let's deal with the optional stuff. As I said above, ifPresent is the way to go here, since we want to do something with side effects. It also provides a way to "extract" the value from an Optional and bind it to a name, but only within the context of a lambda expression. Since we want to do this for three different Optionals, nesting is called for. Nesting allows names from outer lambdas to be captured by inner lambdas. This lets us bind names to values extracted from the Optionals -- but only if they're present. This can't really be done with a linear chain, since some intermediate data structure like a tuple would be necessary to build up the partial results.
Finally, in the innermost lambda, we call the helper method defined above.
void inviteById(UserId targetId, UserId sourceID, EventId eventId) {
userRepository.findById(targetId).ifPresent(
target -> userRepository.findById(sourceID).ifPresent(
source -> eventRepository.findById(eventId).ifPresent(
event -> setupInvite(target, source, event))));
}
Note that I've inlined the Optionals instead of holding them in local variables. This reveals the nesting structure a bit better. It also provides for "short-circuiting" of the operation if one of the lookups doesn't find anything, since ifPresent simply does nothing on an empty Optional.
It's still a bit dense to my eye, though. I think the reason is that this code still depends on some external repositories on which to do the lookups. It's a bit uncomfortable to have this mixed together with the Optional processing. A possibility is simply to extract the lookups into their own methods findUser and findEvent. These are pretty obvious so I won't write them out. But if this were done, the result would be:
void inviteById(UserId targetId, UserId sourceID, EventId eventId) {
findUser(targetId).ifPresent(
target -> findUser(sourceID).ifPresent(
source -> findEvent(eventId).ifPresent(
event -> setupInvite(target, source, event))));
}
Fundamentally, this isn't that different from the original code. It's subjective, but I think I prefer this to the original code. It has the same, fairly simple structure, although nested instead of the typical linear chain of Optional processing. What's different is that the lookups are done conditionally within Optional processing, instead of being done up front, stored in local variables, and then doing only conditional extraction of Optional values. Also, I've separated out data manipulation (extraction of email and name, sending of message) into a separate method. This avoids mixing data manipulation with Optional processing, which I think tends to confuse things if we're dealing with multiple Optional instances.
I think you should consider taking another approach.
I'd start by not issuing the three calls to the DB at the beginning. Instead, I'd issue the 1st query and only if the result is present, I'd issue the 2nd one. I'd then apply the same rationale with regard to the 3rd query and finally, if the last result is also present, I'd send the invite. This would avoid unnecessary calls to the DB when either one of the first two results is not present.
In order to make the code more readable, testable and maintainable, I'd also extract each DB call to its own private method, chaining them with Optional.ifPresent:
public void sendInvite(Long targetId, Long sourceId, Long meetingId) {
userRepository.findById(targetId)
.ifPresent(target -> sendInvite(target, sourceId, meetingId));
}
private void sendInvite(User target, Long sourceId, Long meetingId) {
userRepository.findById(sourceId)
.map(User::getName)
.ifPresent(sourceName -> sendInvite(target, sourceName, meetingId));
}
private void sendInvite(User target, String sourceName, Long meetingId) {
eventRepository.findById(meetingId)
.map(Event::getName)
.ifPresent(meetingName -> sendInvite(target, sourceName, meetingName));
}
private void sendInvite(User target, String sourceName, String meetingName) {
String contents = String.format(
"Hi %s, $s has invited you to $s",
target.getName(),
sourceName,
meetingName);
sendInvite(target.getEmail(), contents);
}
You can use the following if you want to stick to Optional and not commit to consuming the value immediately. It makes use of Triple<L, M, R> from Apache Commons:
/**
* Returns an optional contained a triple if all arguments are present,
* otherwise an absent optional
*/
public static <L, M, R> Optional<Triple<L, M, R>> product(Optional<L> left,
Optional<M> middle, Optional<R> right) {
return left.flatMap(l -> middle.flatMap(m -> right.map(r -> Triple.of(l, m, r))));
}
// Used as
product(maybeTarget, maybeSourceName, maybeEventName).ifPresent(this::sendInvite);
One could imagine a similar approach for two, or multiple Optionals, although java unfortunately doesn't have a general tuple type (yet).
The first approach is not perfect (it does not support laziness - all 3 database calls will be triggered anyway):
Optional<User> target = userRepository.findById(id1);
Optional<String> sourceName = userRepository.findById(id2).map(User::getName);
Optional<String> eventName = eventRepository.findById(id3).map(Event::getName);
if (Stream.of(target, sourceName, eventName).anyMatch(obj -> !obj.isPresent())) {
return;
}
sendInvite(target.get(), sourceName.get(), eventName.get());
The following example is a little bit verbose, but it supports laziness and readability:
private void sendIfValid() {
Optional<User> target = userRepository.findById(id1);
if (!target.isPresent()) {
return;
}
Optional<String> sourceName = userRepository.findById(id2).map(User::getName);
if (!sourceName.isPresent()) {
return;
}
Optional<String> eventName = eventRepository.findById(id3).map(Event::getName);
if (!eventName.isPresent()) {
return;
}
sendInvite(target.get(), sourceName.get(), eventName.get());
}
private void sendInvite(User target, String sourceName, String eventName) {
// ...
}
Well I took the same approach of Federico to only call the DB when needed, it's quite verbose too, but lazy. I also simplified this a bit. Considering you have these 3 methods:
public static Optional<String> firstCall() {
System.out.println("first call");
return Optional.of("first");
}
public static Optional<String> secondCall() {
System.out.println("second call");
return Optional.empty();
}
public static Optional<String> thirdCall() {
System.out.println("third call");
return Optional.empty();
}
I've implemented it like this:
firstCall()
.flatMap(x -> secondCall().map(y -> Stream.of(x, y))
.flatMap(z -> thirdCall().map(n -> Stream.concat(z, Stream.of(n)))))
.ifPresent(st -> System.out.println(st.collect(Collectors.joining("|"))));
You can create an infrastructure to handle a variable amount of inputs. For this to be a good design though, your inputs should not be Optional<?>; but Supplier<Optional<?>> so you can short-circuit the unnecessary evaluation of Optionals while trying to determine whether or not all are present.
Because of this, it'd be better to create a utility wrapper around your Optionals that provides transparent access to the evaluated value using a singleton pattern, like the following:
class OptionalSupplier {
private final Supplier<Optional<?>> optionalSupplier;
private Optional<?> evaluatedOptional = null;
public OptionalSupplier(Supplier<Optional<?>> supplier) {
this.optionalSupplier = supplier;
}
public Optional<?> getEvaluatedOptional() {
if (evaluatedOptional == null)
evaluatedOptional = optionalSupplier.get();
return evaluatedOptional;
}
}
Then you can create another class that handles a List of these wrappers and provides a programmatic API to execute a Function that takes as parameters the evaluated values of the actual optionals, hiding further the users involvement in the process. You can overload the method to execute a Consumer with the same parameters. Such class would look something like this:
class OptionalSemaphores {
private List<OptionalSupplier> optionalSuppliers;
private List<Object> results = null;
private boolean allPresent;
public OptionalSemaphores(Supplier<Optional<?>>... suppliers) {
optionalSuppliers = Stream.of(suppliers)
.map(OptionalSupplier::new)
.collect(Collectors.toList());
allPresent = optionalSuppliers.stream()
.map(OptionalSupplier::getEvaluatedOptional)
.allMatch(Optional::isPresent);
if (allPresent)
results = optionalSuppliers.stream()
.map(OptionalSupplier::getEvaluatedOptional)
.map(Optional::get)
.collect(Collectors.toList());
}
public boolean isAllPresent() {
return allPresent;
}
public <T> T execute(Function<List<Object>, T> function, T defaultValue) {
return (allPresent) ? function.apply(results) : defaultValue;
}
public void execute(Consumer<List<Object>> function) {
if (allPresent)
function.accept(results);
}
}
Finally all you have left to do is to create objects of this class (OptionalSemaphores) using Suppliers of your Optionals (Supplier<Optional<?>>) and invoking any of the overloaded execute methods to run (IF all Optionals are present) with a List containing the corresponding evaluated values from your Optionals. The following is a full working demo of this:
public class OptionalsTester {
public static void main(String[] args) {
Supplier<Optional<?>> s1 = () -> Optional.of("Hello");
Supplier<Optional<?>> s2 = () -> Optional.of(1L);
Supplier<Optional<?>> s3 = () -> Optional.of(55.87);
Supplier<Optional<?>> s4 = () -> Optional.of(true);
Supplier<Optional<?>> s5 = () -> Optional.of("World");
Supplier<Optional<?>> failure = () -> Optional.ofNullable(null);
Supplier<Optional<?>> s7 = () -> Optional.of(55);
System.out.print("\nFAILING SEMAPHORES: ");
new OptionalSemaphores(s1, s2, s3, s4, s5, failure, s7).execute(System.out::println);
System.out.print("\nSUCCESSFUL SEMAPHORES: ");
new OptionalSemaphores(s1, s2, s3, s4, s5, s7).execute(System.out::println);
}
static class OptionalSemaphores {
private List<OptionalSupplier> optionalSuppliers;
private List<Object> results = null;
private boolean allPresent;
public OptionalSemaphores(Supplier<Optional<?>>... suppliers) {
optionalSuppliers = Stream.of(suppliers)
.map(OptionalSupplier::new)
.collect(Collectors.toList());
allPresent = optionalSuppliers.stream()
.map(OptionalSupplier::getEvaluatedOptional)
.allMatch(Optional::isPresent);
if (allPresent)
results = optionalSuppliers.stream()
.map(OptionalSupplier::getEvaluatedOptional)
.map(Optional::get)
.collect(Collectors.toList());
}
public boolean isAllPresent() {
return allPresent;
}
public <T> T execute(Function<List<Object>, T> function, T defaultValue) {
return (allPresent) ? function.apply(results) : defaultValue;
}
public void execute(Consumer<List<Object>> function) {
if (allPresent)
function.accept(results);
}
}
static class OptionalSupplier {
private final Supplier<Optional<?>> optionalSupplier;
private Optional<?> evaluatedOptional = null;
public OptionalSupplier(Supplier<Optional<?>> supplier) {
this.optionalSupplier = supplier;
}
public Optional<?> getEvaluatedOptional() {
if (evaluatedOptional == null)
evaluatedOptional = optionalSupplier.get();
return evaluatedOptional;
}
}
}
Complete code on GitHub
Hope this helps.
If you treat Optional just as a marker for method return values, the code becomes very simple:
User target = userRepository.findById(id1).orElse(null);
User source = userRepository.findById(id2).orElse(null);
Event event = eventRepository.findById(id3).orElse(null);
if (target != null && source != null && event != null) {
String message = String.format("Hi %s, %s has invited you to %s",
target.getName(), source.getName(), event.getName());
sendInvite(target.getEmail(), message);
}
The point of Optional is not that you must use it everywhere. Instead, it serves as a marker for method return values to inform the caller to check for absentness. In this case, the orElse(null) takes care of this, and the calling code is fully concious about the possible nullness.
return userRepository.findById(id)
.flatMap(target -> userRepository.findById(id2)
.map(User::getName)
.flatMap(sourceName -> eventRepository.findById(id3)
.map(Event::getName)
.map(eventName-> createInvite(target, sourceName, eventName))))
First of all you return an Optional as well. It's better to have a method first that creates an invite, which you can call and then send if it's not empty.
Among other things, it's easier to test. Using flatMap you also get the benefit of laziness, since if the first result is empty, nothing else will be evaluated.
When you want to use multiple optionals, you always should use a combination of map and flatMap.
I'm also not using target.getEmail() and target.getName(), those should be safely extracted in createInvite method, since I don't know if they can be nulls or not.
Keeping in mind that Exceptions should not be used in this fashion,
for conciseness you can consider as well:
try {
doSomething( optional1.get(), optional2.get(), optional3.get() );
} catch( NoSuchElementException e ) {
// report, log, do nothing
}
Remember, you can define Classes and Records inline to keep the state explicit and flattened vs. nested using callbacks/closures. It might seem a bit overkill for a small example like this, but it really helps when each nested 'chain' ends up doing work.
For example, given your 3 Optionals using lombok:
#Value #With class Temp {User target; String source; String eventName;}
maybeTarget
.map(target -> new Temp(target, null, null))
.flatMap(tmp -> maybeSourceName.map(tmp::withSource))
.flatMap(tmp -> maybeEventName.map(tmp::withEventName))
.ifPresent(tmp -> System.out.printf("Hi %s, %s has invited you to %s%n", tmp.target.getName(), tmp.source, tmp.eventName));
You can do the same w/records but you'll have to do a bit more work since you have to copy everything by hand:
record TempRecord(User target, String source, String eventName) {}
maybeTarget
.map(target -> new TempRecord(target, null, null))
.flatMap(tmp -> maybeSourceName.map(source -> new TempRecord(tmp.target, source, null)))
.flatMap(tmp -> maybeEventName.map(eventName -> new TempRecord(tmp.target, tmp.source, eventName)))
.ifPresent(tmp -> System.out.printf("Hi %s, %s has invited you to %s%n", tmp.target.getName(), tmp.source, tmp.eventName));
I've tried to keep the data immutable and functions pure.

A method of getting the value of fields without using reflection

I've been given a class with some 200 fields in which their values are read using reflection. It looks basically like this
for (Field f : this.getClass().getFields())
{
try
{
Object o = f.get(this);
if (f.getType() == String.class)
{
//do things with the string
}
}
catch (Exception ex)
{
logger.error("Cannot get value for field. {}", ex.getMessage());
}
}
This works very well for such an unwieldy amount of fields as I suppose is the point of reflection. I've been asked to refactor it because it's slow (is it?).
So far the only method I can come up with his an ungodly amount of hard coding, is there another quick method?
First you should verify with a profiler that it indeed is slow. Reflection is slower than accessing variables normally, but that doesn't necessarily mean that it's the source of slowness.
Provided that you're using setters to modify those values, you can refactor the class to update a Map<String,Object> whenever a setter is called. This provides faster access to the fields than reflection, but may not be possible depending on your use case.
Most of the time is spent in obtaining the Field objects (and possibly filtering them) The actual lookup can be pretty fast. I use ClassValue to cache this information and speed it up.
public enum StringFields {
INSTANCE;
final ClassValue<List<Field>> fieldsCache = new ClassValue<List<Field>>() {
#Override
protected List<Field> computeValue(Class<?> type) {
return Collections.unmodifiableList(
Stream.of(type.getFields())
.filter(f -> f.getType() == String.class)
.peek(f -> f.setAccessible(true)) // turn off security check
.collect(Collectors.toList()));
}
};
public static List<Field> getAllStringFields(Class<?> type) {
return INSTANCE.fieldsCache.get(type);
}
}
So far the only method I can come up with his an ungodly amount of hard coding, is there another quick method?
You can use reflection to get the getters of those fields and generate code which reads out those getters.
The code generation can then be part of a build step.

Java 8 streams map, keep nulls as null

I have this piece of code:
public LatLng[] locationDtoListToLatLngArray(List<LocationDto> locationDtoList) {
return locationDtoList.stream()
.map(locationDto -> new LatLng(locationDto.getLatitude(), locationDto.getLongitude()))
.toArray(LatLng[]::new);
}
but is crashing if locationDto is null inside .map
I fixed it doing this:
public LatLng[] locationDtoListToLatLngArray(List<LocationDto> locationDtoList) {
return locationDtoList.stream()
.map(locationDto -> locationDto == null ? null : new LatLng(locationDto.getLatitude(), locationDto.getLongitude()))
.toArray(LatLng[]::new);
}
but I want to know if there is a better approach (without checking if locationDto == null)
Please note that, if locationDto == null, I want to keep the null, so filter is not an option :)
Thanks
EDIT: I know that the problem is accessing a null object, I just want to know is if there is some function like .map(), that do what I need, .mapKeepingNulls(), something like that.
EDIT 2: I ended up doing this:
public LatLng[] locationDtoListToLatLngArray(List<LocationDto> locationDtoList) {
return locationDtoList.stream()
.map(this::locationDtoToLatLng)
.toArray(LatLng[]::new);
}
private LatLng locationDtoToLatLng(LocationDto locationDto) {
if (locationDto == null) {
return null;
}
return new LatLng(locationDto.getLatitude(), locationDto.getLongitude());
}
The problem is that you are accessing methods of a potentionally null value. If you really don't want the null check there (which I think is a good solution) you can try making a static method in LatLng that will take the LocationDto and return the right instance or null when the supplied LocationDto is null.
Something like this:
public static LatLng getFromLocationDto(LocationDto ldt){
if(ldt == null)
return null;
return new LatLng(ldt.getLatitude(), ldt.getLongitude());
}
But the null check has to be somewhere (unless you can ensure that there will be no null int the locationDtoList).
The issue as little to do with Java 8 streams. You are getting an NullPointerException when doing locationDto.getLatitude().
It is totally normal to check for null value. If you were not in a stream, I am almost sure that you would have not disturbed you.
Maybe what you dislike is the fact that you are performing inline conditional operation in a one-liner, in which case I advise you to use an helper function like _createLatLng(LocationDto locationDto) to externalize that process.
You can make use of Optional, which is a new class in Java 8 made for this purpose.
// transform locationDtoList to a list of Optional
locationDtoList.stream()
.filter(Optional::isPresent)
.map(Optional::get)
.map(locationDto -> new LatLng(locationDto.getLatitude(), locationDto.getLongitude()))
.toArray(LatLng[]::new);

Creating method filters

In my code I have a List<Person>. Attributes to the objects in this list may include something along the lines of:
ID
First Name
Last Name
In a part of my application, I will be allowing the user to search for a specific person by using any combination of those three values. At the moment, I have a switch statement simply checking which fields are filled out, and calling the method designated for that combination of values.
i.e.:
switch typeOfSearch
if 0, lookById()
if 1, lookByIdAndName()
if 2, lookByFirstName()
and so on. There are actually 7 different types.
This makes me have one method for each statement. Is this a 'good' way to do this? Is there a way that I should use a parameter or some sort of 'filter'? It may not make a difference, but I'm coding this in Java.
You can do something more elgant with maps and interfaces. Try this for example,
interface LookUp{
lookUpBy(HttpRequest req);
}
Map<Integer, LookUp> map = new HashMap<Integer, LookUp>();
map.put(0, new LookUpById());
map.put(1, new LookUpByIdAndName());
...
in your controller then you can do
int type = Integer.parseInt(request.getParameter(type));
Person person = map.get(type).lookUpBy(request);
This way you can quickly look up the method with a map. Of course you can also use a long switch but I feel this is more manageable.
If good means "the language does it for me", no.
If good means 'readable', I would define in Person a method match() that returns true if the object matches your search criteria. Also, probably is a good way to create a method Criteria where you can encapsulate the criteria of search (which fields are you looking for and which value) and pass it to match(Criteria criteria).
This way of doing quickly becomes unmanageable, since the number of combinations quickly becomes huge.
Create a PersonFilter class having all the possible query parameters, and visit each person of the list :
private class PersonFilter {
private String id;
private String firstName;
private String lastName;
// constructor omitted
public boolean accept(Person p) {
if (this.id != null && !this.id.equals(p.getId()) {
return false;
}
if (this.firstName != null && !this.firstName.equals(p.getFirstName()) {
return false;
}
if (this.lastName != null && !this.lastName.equals(p.getLastName()) {
return false;
}
return true;
}
}
The filtering is now implemented by
public List<Person> filter(List<Person> list, PersonFilter filter) {
List<Person> result = new ArrayList<Person>();
for (Person p : list) {
if (filter.accept(p) {
result.add(p);
}
}
return result;
}
At some point you should take a look at something like Lucene which will give you the best scalability, manageability and performance for this type of searching. Not knowing the amount of data your dealing with I only recommend this for a longer term solution with a larger set of objects to search with. It's an amazing tool!

Categories