Guava: ImmutableSet of either values or an empty set - java

I need a cleaner solution to using an ImmutableSet. I have code that looks like
Set foo = ImmutableSet.copyOf(aGeoR.getFailed());
it works great when aGeoR.getFailed() returns one or more entries.
it fails when the returned set is null.
When its null, I need a
Set foo = ImmutableSet.of();
What is the clean way to do this?

This is phrased as a question about Guava and ImmutableSet, but the real issue here is with aGeoR.getFailed(). It is essentially never appropriate for a collection-returning method to return null. It should be returning an empty set to begin with (see Effective Java); and yeah, the fact that it isn't is going to cause some pain to users.
When I have to deal with an API like that, and I can't fix it or get it fixed, I do exactly what you showed in your revision of #Jherico's answer.
Set<FailedGeoR> failedOrNull = aGeoR.getFailed();
Set<FailedGeoR> failed = (failedOrNull == null)
? ImmutableSet.<FailedGeoR>of()
: ImmutableSet.copyOf(failedOrNull);

Set foo = aGeoR.getFailed();
foo = foo == null ? new HashSet() : ImmutableSet.copyOf(foo);

I would use an Optional:
Set<FailedGeoR> foo = Optional.ofNullable(aGeoR.getFailed())
.map(ImmutableSet::copyOf)
.orElse(ImmutableSet.of())

I agree #Agustín Ranieri, but if possible, I hope Guava add ofNull and copyOfNull methods, like Optional, and it's just do one more thing, when element is null or empty, always return ImmutableSet.of(), it's let me write less duplicates code.
// the foo always not be null
ImmutableSet<?> foo = ImmutableSet.copyOfNull(aGeoR.getFailed());

Related

What would be the best approach for this Java null check? Should I use an Optional?

I want to achieve the following:
LinkedHashMap<String, String> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("someString", object.getObjectField().getValue());
If object.getObjectField() is null, then I get a NullPointerException. I can easily avoid it, with the following null check:
if(object.getObjectField() != null) {
linkedHashMap.put("someString", object.getObjectField().getValue());
} else {
linkedHashMap.put("someString", "N/A");
}
However, I was thinking to find a better and prettier implementation, using Java 8 Optional, but in this case I still receive a NullPointerException:
linkedHashMap.put("someString", Optional.ofNullable(object.getObjectField().getValue()).orElse("N/A");
What would be the right approach in this case?
but in this case I still receive a NullPointerException
That's because you would still be invoking getValue() even if getObjectField() is null.
You can use Optional.map to apply a function if present:
Optional.ofNullable(object.getObjectField()).map(ObjectType::getValue)
If you want to use a default value, you can just add the orElse after that:
Optional.ofNullable(object.getObjectField()).map(ObjectType::getValue)
.ofElse("N/A")

Java8 : Can't get value of an Optional.of(String) return type

In the context of using the OWLAPI 4.0 API, this following line of code:
ontologyIRI = IRI.create(o.getOntologyID().getOntologyIRI().toString());
returns the following string :
"Optional.of(http://www.indytion.com/music/composition)".
What I need is the sole string "http://www.indytion.com/music/composition".
I tried to declare ontologyIRI as Optional and use .get() method, .orElse(), etc. to no avail. I still have the returned string that includes the 'optional.of()' part.
My question is : How could I get the internal string?
Thank you very much for your help.
Edit : The full code the method
private void LoadOntology(String ontologyPath)
{
OWLOntologyManager man = OWLManager.createOWLOntologyManager();
OWLOntology o;
File ontologyFile = new File(ontologyPath);
Optional<IRI> ontologyIRI;
try {
o = man.loadOntologyFromOntologyDocument(ontologyFile);
ontologyIRI = Optional.of(IRI.create(String.valueOf(o.getOntologyID().getOntologyIRI()).toString()));
System.out.println("Ontology IRI is: " + ontologyIRI.get());
} catch (OWLOntologyCreationException e) {
e.printStackTrace();
}
}
The System.out.println() returns exactly this string:
"Ontology IRI = Optional.of(http://www.indytion.com/music/composition)"
Use .get() instead of toString()
//Returns 'Optional[example]'
Optional.of("example").toString();
//Returns 'example'
Optional.of("example").get();
Short answer: Replace
Optional.of(IRI.create(String.valueOf(o.getOntologyID().getOntologyIRI()).toString()));
with
o.getOntologyID().getOntologyIRI().get();
Longer answer: you're doing an awful lot of back-and forth that's pointless at best and actively harmful in some cases:
In no particular order:
others have already commented that IRI instances are immutable, so creating a new one from an existing one is kind of pointless (if harmless).
calling Optional.of() if you don't intend to actually return an Optional is almost always a bad idea.
String.valueOf() is used to get a string-representation of some value and is usually most useful for debugging, but should not be relied on to fully round-trip everything about an object (the same applies to toString().
So basically what you're left with is this:
o.getOntologyID().getOntologyIRI() gives you an Optional<IRI>
you want an IRI.
Optional::get returns the value contained in the optional, if one exists, so you simply need to call get()
If, however the Optional is empty (i.e. there is no underlying value) then get() will throw a NoSuchElementException. This might or might not be what you want. To work around this either call isPresent() before calling get() to check if a value exists or use any of the multitude of other accessor methods, some of which have "built-in checks" in a way.
Finally, it seems that the problem was not the code itself. This is how the problem has been solved. But I don't understand why it has been solved :
I copy/paste (in the same file) the "shouldAddObjectPropertyAssertions()" example from OWLAPI4 examples -> This example code runs OK (but does not use the getOntologyID() method as I do).
Change SDKs to another minor version '1.8.0_61'
Change again with initial and desired SDK '1.8.0_131'
Invalidate caches and restart the IDE
Problem solved. The exactly same code :
ontologyIRI = o.getOntologyID().getOntologyIRI().get();
System.out.println("Ontology IRI is: " + ontologyIRI);
Now returns the expected string value : "http://www.indytion.com/music/composition" and not "Optional.of(http://www.indytion.com/music/composition)" anymore.
If someone can explain why it has been fixed, I would be very glad.
Thank you again for your help.

How effectively we can code null checks Java 8

Little new to Java 8 style;
How effectively, we can code for the below statement by including all the null checks usin Java 8 API
search.getResource()
.getResults().get(0).getCustomer().getPhoneNumber()
I tried like following: (Looks little weird to me with Optional everywhere)
List<Result> results = search.getResource().getResults();
Optional<Result> optionalResult = Optional.of(results).orElse(new ArrayList<>()).stream().findFirst();
if(optionalResult.isPresent() && Optional.of(optionalResult.get().getCustomer()).isPresent()) {
Source source = Optional.of(optionalResult.get().getCustomer()).get();
Optional<List<Customer>> customers = Optional.of(source.getCustomers());
if(customers.isPresent() && customers.get().stream().findFirst().isPresent() &&
Optional.of(customers.get().stream().findFirst().get().getPhoneNumber()).isPresent())
dest.setNumber(Integer.parseInt(customers.get().stream().findFirst().get().getPhoneNumber())));
}
Could you please suggest me a better way of doing. Thanks!
You can use the map method of Optional class:
final Optional<String> optional = Optional.ofNullable(search.getResource())
.map(resource -> resource.getResults())
.map(results -> results.size() > 0 ? results.get(0) : null)
.map(result -> result.getCustomer())
.map(customer -> customer.getPhoneNumber());
optional.ifPresent(phoneNumber -> {
System.out.println(phoneNumber);
});
OT: Why don't you simply code null safe?
Coding null safe means that you never explicitly retun a (literal) null from a method or pass one in as a parameter. Furthermore you always do you null checks on local variables before you pass them around or return them.
On Map collections you use getOrDefault or computeIfAbsent to replace the null returned for missing keys with a Null Equivalent Constant of the expected type.
Usually passing null back and forth is a in band signal for an error condition which Java has exceptions for.
And no: passing around Optional is not a solution, but another incarnation of the problem. Optional is an elegant way to deal with variables than may contain null within a method.
Don't let it escape...

Is it good idea to use Optional.orElseGet to do some logging logic

I want to use Optional for handling null values, the "tricky" part which I cannot think of what is the best way to do - is that I want to do logging if value is null. I can achieve that with following code - but it feels awkward.
(Update: I have posted my own answer, with Optional from Java 9)
Lets say code looks like this:
// logLine.getSomeProperty returns Optional<String>
List<LogDetails> logDetails = logLine.getSomeProperty()
.map(this::extractLogDetails)
.orElseGet(() -> logError(logLine));
List<LogDetails> extractLogDetails(String s) {
List<LogDetails> logDetails = new ArrayList<>();
String sp = "(?:([A-Z0-9]{5,7})-([A-Z0-9]{9})-(.{4}))";
Pattern p = Pattern.compile(sp, Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(s);
while (m.find()) {
logDetails.add(new LogDetails(m.group(1), m.group(2), m.group(3)));
}
return logDetails;
}
List<LogDetails> logError(LogLine logLine) {
log.error("Error while ... {} ", logLine));
persistence.setErrorStatus(logLine, FAILED_PARSING);
return new ArrayList<>();
}
It would do what I want, but I have several "problems" with it.
I found it odd, that method called orElseGet is used for logging
errors.
I could replace orElseGet with orElseThrow and logError there and DO NOT throw anything - which I don't like either.
logError method returns List which I don't use and it looks weird to return something from method which should be void.
Simply there must be better way
Cases where someProperty is not null, but there are no matches - I would like to log as well, but for that I would need another line of code to check if logDetails.size() == 0
The orElseGet is not really intended as an error handling mechanism, but as a way to generate a different default value in case the Optional instance is not carrying any.
If you want to check if the Optional is empty explicitly, simply use the Optional.isPresent() check, and do the logError() in that case.
What you need to think first is, if the Optional is empty, what do you want to do? Apart from logging the error, do you want to proceed with an empty list?
If yes then you could have something like this:
List<LogDetails> logDetails = logLine.getSomeProperty()
.map(this::extractLogDetails)
.orElseGet(Collections::emptyList);
After which you could do:
if (logDetails.isEmpty()) {
logError(logline);
}
Alternatively, if you do not want to have an empty list at all, you could keep things at optional level. This way, both cases where the getSomeProperty() is empty or when the generated list is empty are handled in the same way.
Optional<List<LogDetails>> logDetailsOpt = logLine.getSomeProperty()
.map(this::extractLogDetails)
.filter(list -> !list.isEmpty());
if (!logDetailsOpt.isPresent()) {
logError(logLine);
}
In both cases, logError() is not expected to return anything. It is doing what it is intended to do in its name, logging the error.
Rather than trying to overuse the functionality of Optional, try to make your intentions in your code clear. There is more value in readability.
Rather than changing result type or logging inside stream you can simply return partitioned Map. Then after obtaining the result, execute log function on the resulting map accordingly.
Map<Boolean, List<String>> map = Stream.of("a", "aaa", "aaaa")
----
.collect(() -> Collectors.partitioningBy(predicate))
----
While I am grateful for the answers, but I just recently find out, that Java 9 introduced new method to Optional and I like it best.
Here is example.
Optional.ofNullable(user).ifPresentOrElse( u -> logger.info("User is:" + u.getEmail()),
() -> logger.info("User not found"));

Java 8 lambda expression is present

First I need to check if data is present in list then get else set default or empty value on a Java 8 stream.
Currently I am using below code without isPresent but I dont know how to use isPresent in java8.
I am trying something below which is not perfect:
String isScheme = (this.mapProgramApproaches.stream().findFirst().isPresent())? this.mapProgramApproaches.stream().findFirst().get().getIsScheme().toString() : "0";
Where as mapProgramApproaches this is set.
Don't use isPresent() (it makes no sense to run the Stream pipeline twice).
You can use map to map the value of the Optional to the required String, and then
use orElse() to return a default value when the Optional value is not present:
String isScheme = this.mapProgramApproaches.stream()
.findFirst()
.map(o->o.getIsScheme().toString())
.orElse("0");
Maybe you are looking for something like this:
String isScheme = this.mapProgramApproaches.stream()
.findFirst()
.map(p -> p.getIsScheme().toString())
.orElse("0");
I'm not sure about context in which you are doing this, but I suppose that you would like to check whether some object is scheme and then do something with that. In that case I would suggest implement it like this:
List<String> mapProgramApproaches = new ArrayList<>();
mapProgramApproaches.stream()
.filter(this::isScheme)
.findFirst()
.ifPresent(this::doYourCode)
.orElse(defaultValue);
It will make your code cleaner. And will help to avoid additional conditionals!

Categories

Resources