RFR(m): 8140281 deprecate Optional.get()
Stuart Marks
stuart.marks at oracle.com
Tue Apr 26 06:06:47 UTC 2016
On 4/25/16 5:46 PM, Vitaly Davidovich wrote:
> We've introduced Optional to avoid this. The problem is that the obvious
> thing to do is to get() the value out of the Optional, but this buys us
> right back into the what-if-the-value-is-missing case that we had with nulls.
>
> Why is this the "obvious" thing? Because a few stackoverflow threads went like
> that?
Well, the Stack Overflow exchange was a caricature. But without looking too
hard, I found this code: [2]
return stringList.stream().filter(...).findFirst().get() != null;
[2] http://stackoverflow.com/q/36346409/1441122
The person, presumably new to Java 8, was able to determine that findFirst()
returned an Optional instead of the desired value, and was able to figure out
enough to call get(), and even thought enough about the possibly-absent case to
check it against null. But the person had to ask on Stack Overflow about how to
solve the problem.
Now I obviously don't know the exact thought process that went on, but get() has
among the shortest of name of the Optional methods, and has no arguments, so it
seems like it ought to be called more more frequently compared to more
specialized methods like ifPresentOrElse() or orElseThrow().
There are other items on Stack Overflow such as these: [3] [4]. But these
probably aren't what you're looking for. :-)
[3] http://stackoverflow.com/a/26328555/1441122
[4] http://stackoverflow.com/a/23464794/1441122
> It's not clear it returns an Optional - it could be returning some other type
> that happens to have a getWhenPresent. What would be clear it's an Optional is
> to have a local of that type and assign find(pk) to it.
Of course there's always a possibility that there's some other API that has
getWhenPresent() on it. There isn't one in the JDK, and I've been unable to find
one elsewhere. So "getWhenPresent" should be different enough to trigger a
different thought pattern, as opposed to a plain "get" which fades into the
background.
The Optional API was designed to be chained, so you don't want to pull it into a
local variable.
> Descriptive names are good, but like Jon, I'm not buying this change. Is it
> really wrong to tell people to read the javadoc? And is that worth the
> deprecation churn? It's going to be needlessly verbose (esp in existing code
> that already checked isPresent) for no strong benefit. If someone is mindlessly
> having their IDE write code for them, this isn't going to prevent poor code.
> For code review purposes, if it's important to call out that something is
> Optional then use the type somewhere explicitly.
Personally I'm generally skeptical of simple name changes, but after having gone
through a bunch of examples of the use of get(), my skepticism melted away. It
really does seem to be misused a significant fraction of the time, possibly even
a majority of the time.
Beginners and Java 8 learners will probably fall into the trap of forgetting to
check. But JDK programmers, who are decidedly not beginners, are writing code
that uses get() unnecessarily:
========================================
http://cr.openjdk.java.net/~smarks/reviews/8140281/webrev.0.langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/DependencyFinder.java.sdiff.html
206 if (source.isPresent()) {
207 executor.runTask(source.get(), deque);
208 }
could be rewritten as
source.ifPresent(archive -> executor.runTask(archive, deque));
========================================
http://cr.openjdk.java.net/~smarks/reviews/8140281/webrev.0.langtools/src/jdk.jdeps/share/classes/com/sun/tools/jdeps/JdepsTask.java.sdiff.html
476 Optional<String> req = options.requires.stream()
477 .filter(mn -> !modules.containsKey(mn))
478 .findFirst();
479 if (req.isPresent()) {
480 throw new BadArgs("err.module.not.found", req.get());
481 }
could be rewritten as
options.requires.stream()
.filter(mn -> !modules.containsKey(mn))
.findFirst()
.ifPresent(s -> throw new BadArgs("err.module.not.found", s));
========================================
http://cr.openjdk.java.net/~smarks/reviews/8140281/webrev.0.langtools/src/jdk.jshell/share/classes/jdk/internal/jshell/tool/JShellTool.java.sdiff.html
1203 String hist = replayableHistory
1204 .subList(first + 1, replayableHistory.size())
1205 .stream()
1206 .reduce( (a, b) -> a + RECORD_SEPARATOR + b)
1207 .get();
could be rewritten as
String hist = String.join(RECORD_SEPARATOR,
replayableHistory.subList(first + 1, replayableHistory.size()));
(It's also not clear to me that the sublist is guaranteed to be nonempty, so the
first snippet might fail in get().)
========================================
http://cr.openjdk.java.net/~smarks/reviews/8140281/webrev.0.jdk/src/java.base/share/classes/java/lang/module/Resolver.java.sdiff.html
100 if (mref.location().isPresent())
101 trace(" (%s)", mref.location().get());
could be rewritten as
mref.location().ifPresent(loc -> trace(" (%s)", loc);
========================================
http://cr.openjdk.java.net/~smarks/reviews/8140281/webrev.0.jdk/src/java.base/share/classes/java/lang/reflect/Layer.java.sdiff.html
364 Optional<Configuration> oparent = cf.parent();
365 if (!oparent.isPresent() || oparent.get() != this.configuration()) {
366 throw new IllegalArgumentException(
367 "Parent of configuration != configuration of this Layer");
could be rewritten as
cf.parent()
.filter(cfg -> cfg == this.configuration())
.orElseThrow(() -> throw new IllegalArgumentException(
"Parent of configuration != configuration of this Layer"));
========================================
If you go to grepcode.com, find the source for Optional.get() -- I used 8u40-b25
-- and then click the little arrow next to get(), you can find a bunch of uses
of Optional.get(). A startlingly large fraction of them can be simplified in a
similar manner to the JDK examples I showed above, bypassing the need to call
get(). (I did a quick check of the first ten examples, and I think about seven
could be improved, depending on how you count.)
It really does look to me like get() is a bad API. It's not people "mindlessly"
having their IDE write code. The problem is that get() is deceptively
attractive. Even people remember to check it, they put the checking around the
get(), instead of looking at Optional's other methods. This is what results in
the needlessly verbose code.
A minority of the cases are justified in using get(). If this is renamed to
getWhenPresent(), it becomes slightly more verbose, but it also specifically
states that the programmer is making an assertion about the presence of a value
-- a worthwhile improvement.
Is it worth the churn? Well, the problem is only going to get worse. I believe
that if we don't deprecate get() now, a few years down the road, the problem
will be so bad, we'll say "I wish we had deprecated it back in JDK 9 when we
were talking about it then." Just like right now we're saying, "I wish we had
fixed it before JDK 8 shipped."
s'marks
More information about the core-libs-dev
mailing list