PROPOSAL: Elvis operator

Stephen Colebourne scolebourne at joda.org
Sat Mar 21 06:17:18 PDT 2009


I'm re-submitting the Elvis operator as a separate proposal to ensure it 
is treated as such. So far I've not heard any arguments against this on 
this list, and there are lots of positives.

I've also added significantly to the rationale, examples and references, 
including the point that many developers and coding standards avoid the 
use of the ternary (requiring if/else), making Elvis a significant 
saving in verbosity.

Stephen

-------------------------------------------------------------------
Elvis Operator for Java
AUTHOR(S):
Stephen Colebourne
primarily written up by Neal Gafter

(Neal Gafter is responsible for the formal write-up[5] of the proposal 
detailed below. However, in private communication he indicated that he 
did not intend to submit it to Project Coin, as indicated in his 
write-up: "[I do] not specifically advocate adding these features to the 
Java programming language.  Rather, this document is offered as an 
example of a language change proposal in a form suitable for 
consideration in the JDK7 small language changes JSR.  Specifically, it 
is more like a specification than a tutorial or sales job.".

As such, this proposal is submitted by myself, thanks to Neal's 
willingness to allow me to reuse his write-up. For the submission, I 
have reworded the advantages/benefits/disadvantages/alternatives 
sections from Neal's original document and added detail to the examples. 
Please see the original[5] to compare Neal's version to mine.)


*OVERVIEW*

FEATURE SUMMARY:
The ?: binary "Elvis" operator results in the value of the 
left-hand-side if it is not null, avoiding evaluation of the 
right-hand-side.  If the left-hand-side is null, the right-hand-side is 
evaluated and is the result.

MAJOR ADVANTAGE:
It is a common occurance in most large systems to find code that checks 
for and handles null. As null is an awkward value to process, a common 
requirement is to provide a default value instead of null, for example 
an empty string or an empty list. The current code for providing a 
default value is more verbose than it needs to be. This proposal 
provides a simple syntax sugar to ease the verboseness.

The second advantage is to reduce the number of NullPointerExceptions. 
These frequently occur late in the development cycle, significantly 
slowing delivery. Academic analysis [6] showed that 5% of the bugs found 
in the release of Eclipse JDT v3.3 were directly due to NPE.

Better null-handling is the most-wanted change in Java based on 
developer polls [1].

MAJOR BENEFIT:
The common coding pattern of checking for null and supplying a default 
value is greatly simplified.

Developers need to default for null in two main scenarios. The first is 
when handling input from APIs that return null. While this may be 
considered by some to be a design flaw in the API, the reality is that 
it is extremely common. Keeping it hard to handle the null value doesn't 
make it any more likely that the API be changed to stop returning null.

The second is when auto-unboxing. The current auto-unboxing feature is 
considered dangerous by some coding shops, who have banned its use as a 
result. This is because it can produce NPE from unexpected places. The 
addition of the Elvis operator provides a simple way for developers to 
handle any potential null value, thus greatly enhancing the value of 
auto-unboxing. (Currently, a developer has to write an if statement, 
which introduces an extra block which entirely defeats the purpose of 
the 'convenience' unboxing).

The simplification of the code necessary to default for null is also 
likely to have positive side effects. Because the code is simpler to 
write, developers will be more likely to include the defaulting of null. 
This will have the benefit of reducing the number of NullPointerExceptions.

The JSR-305/208 project is proposing adding nullable annotations to 
Java. The Elvis operator would dovetail nicely with this work, as it 
would provide a safe way to convert from a @Nullable to a @NotNull variable.

Finally, the proposed operator will, in certain cases, generally be 
slightly more performant and correct code than that written by hand. 
This is because the LHS of the expression will only be evaluated once 
with the proposed change, whereas a developer will normally evaluate it 
twice (ie. consider the case where the LHS is a method call not a simple 
variable).

MAJOR DISADVANTAGE:
Associated costs in documentation, tutorials and overall language size.

The principle perceived disadvantage, is that it encourages, rather than 
discourages, the use of null values in APIs. No one is disputing that 
empty arrays or empty collections should be returned from APIs rather 
than nulls, however that is only a small proportion of the
returned types in any large system. Many large systems consist of large 
numbers of JavaBean type objects which may have null values for many of 
their fields (representing an absence of information, invalid data, 
etc.). In these cases, null is a suitable and valuable value to hold in 
those fields, and is widely used as such. Accessing the resulting data 
for use often requires defaulting the null value, and that is where this 
proposal comes in.

ALTERNATIVES:
Use the ternary expression, as today. However, since many developers and 
coding standards argue against the ternary [7], it may be necessary to 
handle the defaulting of null in an if/else and 2-8 lines of code 
(depending on how you like your braces). Whether using the ternary or an 
if/else, the important business logic is hidden by the need to handle 
the low-level null issue.

It is possible to solve this issue using a library, such as 
Utils.defaultValue(value, valueIfNull). This is still verbose and 
intrusive, possibly more so than just writing a ternary expression.


*EXAMPLES*

SIMPLE EXAMPLE:

   String s = mayBeNull ?: "null";

whereas, today this is written:

   String s = (mayBeNull != null ? mayBeNull : "null");

or (since many developers and coding shops disapprove of the ternary [7]):

   String s;
   if (mayBeNull != null) {
     s = mayBeNull;
   } else {
     s = "null";
   }


Auto-unboxing example:
   Integer ival = ...;  // may be null
   int i = ival ?: -1;  // no NPE from unboxing


ADVANCED EXAMPLE:
   private Map<String, Integer> hitCounts = ...
   public synchronized void countPageHit(String pageName) {
     int count = hitCounts.get(pageName) ?: 0;
     hitCounts.put(pageName, ++count);
   }

Without this feature, a developer would currently write **:

   public synchronized void countPageHit(String pageName) {
     Integer countVal = hitCounts.get(pageName);
     int count = (countVal != null ? countVal : 0);
     hitCounts.put(pageName, ++count);
   }

or:

   public synchronized void countPageHit(String pageName) {
     Integer countVal = hitCounts.get(pageName);
     if (countVal == null) {
       hitCounts.put(pageName, 0);
     } else {
       hitCounts.put(pageName, ++countVal);
     }
   }

** In fact I suspect that a fair few developers would forget the null 
check at first and just assign to int, resulting in a NPE during testing


*DETAILS*

SPECIFICATION:
Lexical:

We do not add any tokens to the language.  Rather, we introduce new 
operators that are composed of a sequence of existing tokens.

Syntax:

The folllowing new grammar rules are added to the syntax

ConditionalExpression:

ElvisExpression

ElvisExpression:

ConditionalOrExpression ? : ConditionalExpression

Semantics:

An Elvis expression e1?:e2 first evaluates the expression e1.  It is an 
error if this is not a reference type.  If the result is non-null, then 
that is the Elvis expression's result.  Otherwise, e2 is evaluated and 
is the result of the Elvis expression.  In either case, the type of the 
result is the same as the type of ((e1 != null) ? e1 : e2).  [Note: this 
section must mention bringing the operands to a common type, for example 
by unboxing when e2 is a primitive, using the same rules as the ternary 
operator]

Exception Analysis:

No change

Definite Assignment:

JLS section 16.1 (definite assignment and expressions) is augmented with
the following new subsections

16.1.x Elvis Operator

      * v is definitely assigned after e1?:e2 iff v is definitely
assigned after e1.
      * v is definitely unassigned after e1?:e2 iff v is definitely
unassigned after e2.
      * in an expression of the form e1?:e2, v is [un]assigned before e2
iff v is [un]assigned after e1.


COMPILATION:
These new expression forms can be desugared as follows:

      * e1?:e2 is rewritten as (t != null ? t : e2)

where t is a new temporary that holds the computed value of the
expression e1.

TESTING:
This feature can be tested by exercising the new expression form, 
verifying the correct behavior in erroneous and non-erroneous 
situations, with or without null as the value of the left-hand operand, 
with or without primitives, and with respect to definite assignment.

LIBRARY SUPPORT:
No library support is required.

REFLECTIVE APIS:
No reflective APIs require any changes.  However, the not-yet-public 
javac Tree APIs, which describe the syntactic structure of Java 
statements and expressions, should be augmented with a new tree form for 
this new expression type.

OTHER CHANGES:
No other platform changes are required.

MIGRATION:
No migration of existing code is recommended.  These new language 
features are mainly to be used in new code.  However, IDEs should 
provide refactoring advice for taking advantage of these new operators 
when existing code uses the corresponding idiom.


*COMPATIBILITY*

BREAKING CHANGES:
No breaking changes are caused by this proposal.

EXISTING PROGRAMS:
Because the changes are purely the introduction of a new expression 
form, there is no impact on the meaning of existing code.


*REFERENCES*

EXISTING BUGS:
Related, though not exact matches:
6341875: New for loop should treat null as an empty list
6303028: Conditional operator + autoboxing throws NullPointerException
6212662: Boxing/Unboxing detector for == that will always fail

URL FOR PROTOTYPE:
No Java prototype exists at this time.  However, Groovy[2] and Fan[3] 
(among others) have the Elvis operator.

OTHER REFERENCES
[1] Summary of three recent language change polls showing better null 
handling as a key developer request - 
http://www.jroller.com/scolebourne/entry/jdk_7_language_changes_everyone
[2] Groovy Operators - http://groovy.codehaus.org/Operators
[3] Fan operators - 
http://fandev.org/doc/docLang/Expressions.html#nullConvenience
[4] Stephen Colebourne's brief on null-safe operators - 
http://docs.google.com/View?docid=dfn5297z_3c73gwb
[5] The version of this proposal written by Neal Gafter - 
http://docs.google.com/Doc?docid=ddb3zt39_78frdf87dc&hl=en
[6] Academic analysis of nulls, 
http://users.encs.concordia.ca/~chalin/papers/2006-003.v3s-pub.pdf
[7] Avoiding or limiting use of the ternary:
http://users.csc.calpoly.edu/~jdalbey/SWE/code_std.html
http://www.coderanch.com/t/408524/Java-General-beginner/java/Ternary-operator-with-if-elseif
http://www.aptana.com/dev/index.php/Java_Coding_Standard
https://jjguidelines.dev.java.net/book/html/apas04.html
http://qpid.apache.org/java-coding-standards.html






More information about the coin-dev mailing list