7 ms·
Optionals are a huge step forward for Java, even if they aren't perfect. They let you write interfaces that say "I might not have an answer and if you don't dea
by mabbo 10y ago
Optionals are a huge step forward for Java, even if they aren't perfect. They let you write interfaces that say "I might not have an answer and if you don't deal with it that's your problem". That's important.
That they also allow mapping, filtering, etc, isn't about 'removing ifs' or 'hiding ifs' so much as they are about writing more readable code, imho.
Optional<Foo> myValue = gateway.callThatApi(...)
return myValue.filter(Utils::isNotTooShabby)
.map(this::mapToCoolerType)
.map(getDecorator())
.orElseThrow(new TotallyBlewItException())
Is this perfect and beautiful? Nah. But it's better than the 20 lines of Java 7 code I'd need to do the same thing. I'm able to write simple predicates and mapper functions as class variables, dynamically if I want, and call them in order as I like. It's short, it's descriptive rather than prescriptive. It isolates what I want from how I do it.
Debugging is annoying, yes, but I think there's hope that a good pattern for it will be figured out by the community.
- Retric 10y agoWhat's wrong with generics here? ListCondi<ObjType> myValue = gateway.callThatApi(...) foreach(tmp as Condi in myVlue) { if (tmp.worked) { foo(tmp.Generic);} else {bar(tmp.Generic);} } PS: Sorry, have not touched java in like 10 years, but I assume you can have that foreach as a map.
- mabbo 10y agoIn my case above, there's just one object, maybe (or maybe there isn't?). In the case you're presenting, one might consider: Stream<Foo> myVals = gateway.callThatApi(...) return myVals.forEach(val -> getFooOrBar(val).apply(val)) Not quite as nice as Scala would let me do it, but closer, simpler. Streams also let you add filters to the "list" of values, reduce them to a single value with a reducer, or use a nice set of "collectors" that reduce to Java generic collection types. (Collectors.groupBy is so handy). Streams are also lazy, which I think is more good than bad. The thing I tell everyone to do right now, today, is to open their Java code and search for "return null", then replace it with "return Optional.empty()". It'll break your code in lots of places, but things will be better when you fix all those things. Often you'll find lots of places that didn't handle the null possiblity at all!
- gravypod 10y agoWhy is Optional<> better then throwing an exception or returning a null? The way I see it it's like this: Optional<Integer> num = getSomeRiskyNumber(); if (!num.isPresent()) return ... code to bubble up a blank optional vs Integer num = getSomeRiskyNumber(); if (num == null) ... throw exception or return null I get that the author has adopted a more functional programming methodology for dealing with their data but for some tasks this isn't acceptable. To just return 1 value you've allocated at least one object (Optional) and make at least 2 function calls on it (isPresent() and get()). You get `if (value == null)` for free. Throwing exceptions is very heavy and I'd place Optional<> above that but there isn't any way to signify the error that you acctually got. You'd need to make an Optional<Maybe<T>> where Maybe<T> supports error/exceptions. Maybe Maybe<T> holds data and you can extend it with BadMaybe<T> who extends exception or something so you can put that in instead of your value to signal your exception. I don't see the benifit. Maybe I'm just crazy but `if (v == null)` has all the features Optional has for less of an overhead and less cognitive load. (If you're afraid of NPEs then just document all the return states of your methods and use @nullable to show when you need to check. IIRC IntellJ catches that kind of mistake).
- chii 10y agoyou shouldn't really be running `optional.isPresent()`, but rather, use a map operation, or a flatMap operation, all the way until you need to interface an outside api (in which case, you use a `getOrElse(defaultValue)`). You're right about code that branches based on `isPresent()` is semantically the same as null checks - and if you only did that and didn't use any functional paradigm like mapping on the types, you don't really get any real benefit!
- mark242 10y agoIn Scala, you're encouraged to write more bulletproof code by avoiding exceptions at all time, simply because it's easier to realize that (in your case) getSomeRiskyNumber returns an option instead of an integer. getSomeRiskyNumber map { case Some(i) => Map("risk" -> i) case None => Map("error" -> "Error retrieving number.") } map { response => Json.toJson(response) } Reading this code you know you're always going to generate a json response to the user, and depending upon what getSomeRiskyNumber() returns, the json response will either look like { "risk": 23 } or { "error": "Error retrieving number." }. No exceptions needed.