Deprecating: java.util.Optional.get()?
royvanrijn.com
royvanrijn.com
if (opt1.isPresent() && opt2.isPresent() && opt3.isPresent()) {
int i1 = opt1.get();
int i2 = opt2.get();
int i3 = opt3.get();
}
of course scala has a better way of handling that: for (i1 <- opt1; i2 <- opt2; i3 <- opt3) yield (i1, i2, i3)
But java has no support for generators.
Edit: btw. even Scala has `get()` and it was there a long time even when you need it even less there: http://www.scala-lang.org/api/2.11.8/index.html#scala.Option...The proposal is to rename the method[0], not to remove it entirely. So the code you're showing would still be possible, only clearer that it's not innocuous (the body relies very very strongly on the conditional)
[0] not sure why adding an override to #orElseThrow to throw a default exception is not on the table
(An even better solution is SWIFT's approach to nullable types, but that boat has sailed; intellij etc could however implement a static checker for bare Optional-getting)
I think the options orElse/orElseGet and orElseThrow are enough to replace every get() method, and they'll force you to think about the missing scenario.
If you want to do something in case it is present, use ifPresent(lambda).
In case you want to return something, use orElse/orElseGet or orElseThrow, or just return the Optional itself.
orElseThrow doesn't force you to think about the missing scenario, only to think about which exception you want to throw, which in many case you don't care for.
If orElseThrow had an override throwing NoSuchElementException by default it would be a perfect replacement, alas it does not.
It doesn't quite, though it is way too short and convenient.
> If you need to extract the value, the orElse method should be suitable on all cases.
orElse is incorrect if you have specific expectations. get is an assertion, and used in that manner it's perfectly sensible and orElse is not. Plus orElse can send the wrong signal.
> Including it in the first place was as much of a mistake as the null pointer.
The only language I know which has optionals and doesn't have something equivalent to .get() is Elm, and even there you can trivially pattern-match and call Debug.crash in the Nothing case.
Option.get leads to false confidence. I've had many arguments over use .get (in Scala) where another developers argument is "we check that it's defined higher up in the code". At that point a harmless looking refactor by someone else can introduce NPEs.
As long as Optional#isPresent exists, you can have "concrete expectations" of an optional's content (or lack thereof).
> At that point a harmless looking refactor by someone else can introduce NPEs.
The fundamental problem is not .get, it's that you're qualifying code changes involving partial function as "harmless-looking" and "refactoring"
I'm all for strong type systems, but I also acknowledge their limitations.
The issue here stems from the existing community's culture, one especially full of trivially safe getters.
With null, the actual error can show up somewhere completely different in your code making it hard to trace where the null value came from. With .get() the errors happens at exactly the point where I told the compiler: "trust me, this isn't empty!" but was wrong.
The pervasive lack of defensive programming slays me.
My strategy is to use Enums. And no autoboxing. And use NullObjects.
1. Assume parameters aren't null, and then everything breaks and you fix it.
2. Assume ANY object can be null, and check them all, in every single method, and create correct handling in each case. Basically no one does this.
3. Try to figure out which things can be null or not depending on context. Sometimes you are still wrong, and get NPEs. This is what people actually do.
By defensive programming, do you mean #2? Because that kinda sucks compared to just using Optionals.
#2 Optionals are syntactic sugar for null checks.
#2 is what I do. I'm not a great programmer, but keeping this sort of discipline means that my code has fewer trivial (and time-wasting) bugs.
use std::net::Ipv4Addr;
fn main() {
let addr: Ipv4Addr = "0.0.0.0".parse().unwrap();
}
Here, I _know_ that the call to `parse()` will always succeed. More verbose handling isn't helpful.(We almost changed unwrap to assert to make it even more clear, but not everyone agreed that it was more clear)
What's the representation of an optional then? Doesn't make use of some generic concept like subclasses or unions? Surely you have a generic syntax for "assume this union is this particular member" or "assume this base class is this particular subclass" or the like?
> I'm not sure that it's any more verbose or scary looking, though.
I guess the point is that I want all cast-like operations to look alike (or at least to have a way to consistently distinguish them from ordinary methods) so that I can flag them all up consistently in code reviews, linters etc.
Lints for unwrap already exist :)
Do you not have a generic syntax for unwrapping a tagged union, forcibly assuming that it's one particular component? That seems like something users would want.
(More specifically, it's a method which uses `match` internally to do this)
In practice, "add a new language feature" isn't a real solution for programmers who need to get the job done. Static type systems always have limitations. You need to be able to circumvent the type system if it isn't expressive enough for your use case.
b) get() is useful and less verbose for cases where other logic has proven that the get() will succeed.
boolean allPresent = a.isPresent() && b.isPresent()
if (allPresent){
doSomething(a.get())
}
edit: just noticed this example is the same idea merb posted earlier. Database.readOrder()
.map(OrderEngine::process)
.filter(ProcessResult::succeeded)
.ifPresent(Database::storeResult);
Is more readable than the supposed "unpleasant old skool": Order order = Database.readOrder(); //can be null
if(order != null) {
ProcessResult result = OrderEngine.process(order);
if(result != null && result.succeeded()) {
Database.storeResult(result);
}
}
Maybe I'm "old skool" but I find the second much easier to read than the first.However, the second version is a lot more brittle than the first. It's relatively easy to change the code and still get it to compile, whereas you'll have a lot more trouble doing that with the first. For example:
1. If you forget to check 'order' for null, it'll still compile fine, because null is a valid value for type 'Order'. But if you try to feed an Optional<Order> to OrderEngine, it won't compile.
2. If you forget to check 'result' for null, it'll still compile fine, and then throw an NPE when you run 'result.succeeded()'. Again, if you try to call check ProcessResult::succeeded in the first example, it won't compile unless you deal with the empty-result case specifically.
3. Say that later on you want to get your stored values back out of the database... and you find a null in there. Where did it come from? If you forget to check 'result' for null and don't do the 'succeeded()' check, you can put a null in the database. Then your program will work fine until you try to call a method on it, at which point it throws an NPE far, far away from the point where you store it. By using Optional and dealing with the empty case, your code won't compile if you try to store null in the DB.
So unfortunately it will be less readable until Java devs adjust themselves to using this new type, but the benefits do exist.
Background: many moons ago when I was still in school and VB was a valid choice of programming language I thought I new object oriented programming since my VB skills was decent (for a student) and VB had objects.
I have learned a lot since then and suspect something similar is happening and will happily admit that good stuff can be waiting for me as soon as it "clicks" when I get time to sit down with it or get a colleague who knows it and explain it (or when someone on HN gives me a good explanation : )
There is already orElse(value) and orElseThrow(exception), so why not add a plain orElseThrow() to raise the default exception?
But the Java community should move towards ifPresent/map/filter/flatMap etc.
I've been using NullObject since (checking...) 1996. This is The Correct Answer.
http://c2.com/cgi/wiki?NullObject
I first read about NullObject here:
Object-Oriented Design Heuristics http://amzn.to/1ND1YSU
What'd be really neat is some mojo to remove the boilerplate of implementing NullObjects.
What's the difference between "baked into the language" and a language with built-in support (added in Java 8) for pluggable, inferrable type systems (of which @NotNull is just one, pretty basic, example)?
Being the new COBOL, Java is best when it just works. Anyone who can use annotations responsibly will be happier choosing a grown up language like Clojure.
In truth, I don't have an answer for why method?method?property syntax is better than using annotations. Because I tend to use NullObjects and avoid method chaining, to me it's a false choice.
Edit: Concision.
Don't conflate annotations (a compile time construct) with how they can be used. They can be used in many ways. When used as runtime metadata they can be "magic". But @Nullable/@NonNull is (or rather, can be used as) one of Java 8's pluggable type systems, inferred and and checked at compile time[1].
[1]: http://types.cs.washington.edu/checker-framework/current/che...
It's NullObjects all the way down. Makes more sense when designs favor compositions over inheritance.
Optional is the wrapper no one needed. For syntactic sugar, static library methods worked just fine.
class Widget {...}
class final NullWidget extends Widget {
static NULL_WIDGET = new NullWidget();
...
}
class WidgetMap extends HashMap<String,Widget> {
Widget get( String key ) {
if( contains( key )) { return super.get( key ); }
return NULL_WIDGET;
}
...
}(I still don't quite get the point of Optional; you're replacing a direct pointer with a pointer to a pointer, but the outermost pointer can still be null, so you are still vulnerable to a NullPointerException, and now you have two "not present" values to check for: null and !isPresent().)
This is true, but there is a distinction: if your Optional value is null, for any reason, then it's a programming error and should be fixed. If a non-Optional value is null, there's no telling whether it's "allowed" to be null or not. So I never null-check my Optionals in Java -- I'd rather they fail so I can be alerted to them.
And if “usable” has any additional meaning besides “is not null”, this entire machinery is useless because one STILL has to figure out the state of the data!
For example, what if you are sanitizing input from a user and there is a “wall” in your code beyond which a string is considered safe (e.g. decoded, evaluated by regex, or whatever is supposed to happen)? Or, what if “usable” means that a server has been connected to, or a database opened, or a calculation completed, or whatever else you want to say? What if it’s a numerical denominator and you want it to be nonzero? The list could go on and on, and none of these architectures deals with ANY of those possibilities.
And classes like Optional have long existed to solve most of the issues you bring up, see Gwt's SafeHtml, Futures, etc.
That's inaccurate. In fact Optional makes this situation (having to check if data is not null and valid) even easier, because you can simply filter/map the value without having to first check that it is non-null. For example:
possibleResult.filter(res -> isValid(res))
.ifPresent(res -> doSomething(res));Took me to learn a pure functional language (Scala) to come back and start using map, filter, etc ... Not because Java 8 doesn't support it, but because a functional language community just has that mindset. There are many Java 8 developers using streams() and optionals with an imperative programming mindset still.
There could be a discussion to remove `get` altogether, but I don't think it would be a good idea. I don't use the Java type, but `scala.Option` and I think `get` is occasionally helpful in unit tests, scripts, etc.
It's not unsafe, it safely throws an error when the optional is empty.
There already is a #orElseThrow(Exception), there could be a #orElseThrow() defaulting to raising NoSuchElementException.
So each of them has a potential for an unexpected and transient failure that may be returned either via an exception or via a null result.
In my own experience with systems built on top of multiple services (not micro in my case but enough services...) unexpected nulls or incorrectly handled exceptions from transient/network failures in the services depended on is a common mechanism that surfaces bugs in production code.
And these bugs rarely show up in testing unless someone went out of their way to simulate the failure(s). Optionals have been a good way for us to convey that possibility to the calling code and force the consumer to consider the possibility and plan an appropriate response (we're big fans of fail-fast).
I can't remember who said this originally, but it was something like "The first rule of distributing your application is don't." The implication is you should distribute by business concern, not by tech concern.... which could be done with microservices, but it's not how they're being used right now.
http://www.drdobbs.com/errant-architectures/184414966
Just skimmed through the article and found myself nodding all the way through.
Also: I'm not really buying into the whole microservices movement, it is the latest silver bullet.