PROPOSAL: Elvis operator (original) (raw)
Olivier Chorier lapsus63 at gmail.com
Sat Mar 21 06:56:02 PDT 2009
- Previous message: PROPOSAL: Elvis operator
- Next message: PROPOSAL: Elvis operator
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
I initially thought it was a good idea, but after a bit reflection, I don't think it is a real 'plus' compared with the ternary operator.
For example, how would you simplify those lines of code (assuming the coder loves ternary operator) :
int value = object == null ? -1 : (object.getSubObject() == null ? -1 : (object.getSubObject().getValue() == null ? -1 : object.getSubObject().getValue()));
Does somebody has an idea (excepting using an ugly try-catch statement) to write this much clearer ? (sorry for code indentation)
2009/3/21 Stephen Colebourne <scolebourne at joda.org>
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/jdk7languagechangeseveryone [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=dfn5297z3c73gwb [5] The version of this proposal written by Neal Gafter - http://docs.google.com/Doc?docid=ddb3zt3978frdf87dc&hl=en [6] Academic analysis of nulls, http://users.encs.concordia.ca/~chalin/papers/2006-003.v3s-pub.pdf<http://users.encs.concordia.ca/%7Echalin/papers/2006-003.v3s-pub.pdf> [7] Avoiding or limiting use of the ternary: http://users.csc.calpoly.edu/~jdalbey/SWE/codestd.html<http://users.csc.calpoly.edu/%7Ejdalbey/SWE/codestd.html> http://www.coderanch.com/t/408524/Java-General-beginner/java/Ternary-operator-with-if-elseif http://www.aptana.com/dev/index.php/JavaCodingStandard https://jjguidelines.dev.java.net/book/html/apas04.html http://qpid.apache.org/java-coding-standards.html
- Previous message: PROPOSAL: Elvis operator
- Next message: PROPOSAL: Elvis operator
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]