Option-izing Java getters - java

When working with Java from Scala, we have to account for null.
HttpServletRequest getters (getAttribute, getHeader, etc.) for example, all potentially return null.
I know I can manually do a case/match or map operation on each call to an HttpServletRequest method, but that's a bit tedious. Also, method calls like request.getHeader("Accept-Encoding") are a mouthful.
I came up with an enrichment to handle both issues:
class Servlet_Request_Provides_NullSafe_Getters (r: HttpServletRequest) {
def header(s: String) = Option( r.getHeader(s) )
def attrib(s: String) = Option( r.getAttribute(s) )
def remoteHost = Option( r.getRemoteHost )
def accepts = header("Accept-Encoding")
}
implicit def request2Option(r: HttpServletRequest) =
new Servlet_Request_Provides_NullSafe_Getters(r)
1) Is there another/better way than enrich-my-library to achieve the same/similar affect?
2) If this is "the" way to go, what are the performance impacts/risks? In other words, can one get burned by the apparent utility/conveniences of this pattern?
Sorry if this stuff is obvious, only just started enriching the other day, and it really seems quite useful. Just want to make sure I'm applying the pattern in the proper scenarios...
EDIT
#dhg pointed out that Option.apply() and:
def safe[T](v: T) = v match {
case null => None
case x => Some(x)
}
are equivalent, so the getter methods now use Option(f()) instead of my extraneous safe(f()) wrapper
Thanks

As already mentioned in the comments:
def safe[T](v: T) = Option(v)
Option(v) is equivalent to:
v match {
case null => None
case x => Some(x)
}
Also the safe method is unnecessarily public and part of the class. I would suggest simply inlining it.
2) If this is "the" way to go, what are the performance impacts/risks?
Generally adapting Java legacy APIs to utilize Option is a good idea. I do this often with EntityManager.find() that can return null.
Your implicit conversion is also fine. However don't use underscores in class names, Java/Scala naming conventions prefer CamelCase.

Related

Why doesn't Mono.fromRunnable return Mono<Void>?

Runnable returns void in Java. Why does Mono.fromRunnable return Mono<T> instead of Mono<Void>?
API documentation of Mono#fromRunnable states about the type parameter:
The generic type of the upstream, which is preserved by this operator
It allows to use it as part of an operation chain without altering the resulting type.
Example:
This code:
Mono<String> myMono = Mono.empty();
myMono = myMono.switchIfEmpty(Mono.fromRunnable(()
-> System.err.println("WARNING, empty signal !")));
String value = myMono.block(Duration.ofSeconds(1));
System.out.println("Exported value is "+value);
Produces:
WARNING, empty signal !
Exported value is null
The code above compiles fine, and provide a Mono for a String without having to add additional casts.
The posted example is not very good, but I suppose that this signature allows to use fromRunnable to launch side-effects in some case, without disturbing value type of the overall operation chain.
It's kinda like Mono.empty() with additional computing embedded with it.

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 to pattern match a tuple of Option with defined type in Java with Vavr

new Vavr user here.
I am trying to pattern match a tuple of options to execute a statements if both of them are Some, in Scala I would have done this with:
val maybeThis: Option[String] = ???
val maybeThat: Option[String] = ???
(maybeThis, maybeThat) match {
case (Some(dis), Some(that)) => ???
case _ => ???
}
In Java, I am trying this approach:
Tuple2<Option<String>, Option<String>> tuple = new Tuple2<>(Option.of(...), Option.of(...));
return Match(tuple).of(
Case($Tuple2($Some($(instanceOf(String.class))), $Some($(instanceOf(String.class)))),
(someThis, someThat) -> methodWhichEatsTwoStrings(someThis.get(), someThat.get())),
Case($(), t -> run(() -> {
throw new NullPointerException(...);
})));
However, with this implementation the compiler complains that it was expecting Some<Object> instead of Some<String>, same error happens if I omit the $(instanceOf(String.class) in the pattern.
I am moderately sure this is merely a problem of proper syntax, yet I am struggling to find the correct documentation.
Where am I wrong?
Thanks
There's a vavr API construct called for comprehension that tries to mimic Scala's for comprehensions to the extent that it's possible to do in Java. With that construct you could solve your problem quite elegantly. See the For overload for handling two Options for more details. Here's an example code snippet:
String methodWhichEatsTwoStrings(String v1, String v2) {
return v1 + v2; //combine the two values in some way
}
...
Option<String> option1 = Option.some("value1");
Option<String> option2 = Option.some("value2");
String combined = For(option1, option2)
.yield((v1, v2) -> methodWhichEatsTwoStrings(v1, v2))
.getOrElseThrow(() -> new NullPointerException("reasons"));
Of course, you could use Option wrapping values of different types for option1 and option2, or combine multiple options, not just two. You could also use a different type for the return value of the yield function as well. I used String everywhere for the sake of simplicity and to conform to your original example.
I would like to add that I would try to avoid throwing NullPointerException in case one or both of the options are empty. Maybe try to use another vavr data type like Either to represent such an error case?

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"));

How to add type constraints to parameterSpec for method

I'm trying to do exactly what the title says -- I'd like to generate a method spec that looks something like:
public void doSomethingWithThis( Container<? extends ImportantInterface> argument ) {
//1. Collect UnderPants
//2. ...
//3. Profit
}
I understand I can just use the raw type, but generated thing will be consumed by others down stream, and having the type info pop up in their IDEs ( and mine for that matter :/ ) would make my bug solving life easier down the line...
So, I'm a toolbox and 7 more minutes digging around found the path to the answer. The question in the comment points in the right direction, though it's using ParameterizedTypeName.create() which is now ParameterizedTypeName.get()
Code for sample purposes because someone else might find this useful.
ClassName containerClassName = ClassName.get(Container.class);
TypeName wildcardTypeName = WildcardTypeName.subtypeOf(ImportantInterface.class);
ParameterizedTypeName parameterTypeName = ParameterizedTypeName.get(containerClassName, wildcardTypeName);
classBuilder.addMethod(MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.addParameter(parameterTypeName, "cargo")
.addStatement(CodeBlock.builder()
.addStatement("//1. Collect Underpants")
.addStatement("//2. ...")
.addStatement("//3. Profit!!!")
.build())

Categories

Resources