New JEP: Switch Expressions for the Java Language
Jan Lahoda
jan.lahoda at oracle.com
Mon Dec 11 18:03:22 UTC 2017
On 11.12.2017 13:04, Stephen Colebourne wrote:
> At first, my reaction was "great!", but after doing some analysis I'm
> left feeling this isn't going to work well as proposed...
>
> To summarise the proposal, expression switches get:
> - comma separated OR values
> - case null
I believe the current proposal is to add these both (comma separated OR
values and case null) to the switch statement as well. The prototype
supports the case null for both switch statement and expression and the
comma separated OR values are currently only supported in the switch
statement in the prototype (implementation for the switch expression is
pending).
Jan
> - no fall through
> - enforced default
> - auto default clause for enums
>
> And statement switches get none of these (AFAICT).
>
> But if expression switches have no requirement to use the return value
> (as per the rest of Java), then an expression switch can be used in
> all circumstances that a statement switch is used today. And since the
> expression switch would essentially be better than the statement
> switch in every way, I believe best practice would effectively
> deprecate statement switches in most cases. This appears to mean that
> switch is being bifurcated, despite the explicit desire not to. So,
> I've taken a look at what the impact of that on our codebase would be:
>
> ---
>
> I examined the OpenGamma private codebase to see how we use switch.
> The vast majority of uses are from Joda-Beans as follows:
>
> switch (propertyName) {
> case "firstLayer":
> return firstLayer;
> case "secondLayerType":
> return secondLayerType;
> default:
> throw new NoSuchElementException("Unknown property: " + propertyName);
> }
>
> This pattern could be converted to the proposed expression switch,
> which would be safer, but not necessarily easier to read (readability
> varies depending on how short the expression is).
>
>
> There were 111 examples that were not Joda-Beans generated. These can
> be classified as follows:
>
> - Mostly (maybe 80%+) "switch and return a value, throw if unknown" (as above).
>
> - Some examples (maybe 10%) which were dying to be expression switches
> (assign a local variable and set it up using the switch).
>
> - Occasional example where two or three variables were being assigned
> by the switch (something that expression switch could not handle
> without using a messy Pair/Triple).
>
> - A few examples where the switch is to cause the program flow to
> dispatch to disparate pieces of logic. These cases had to use "break",
> but would not be easily convertible to expression switches. A similar
> example is where a Map or List was being updated by the switch.
>
> - One case where it is essentially an expression switch, but the
> default clause returns from the method instead of throwing.
>
> - One case using fall through. Meh.
>
> For info, here is a more tricky one that uses a mutable local variable:
>
> private static Pattern wildcardsToPattern(String text) {
> StringTokenizer tkn = new StringTokenizer(text, "?*", true);
> StringBuilder buf = new StringBuilder(text.length() + 10);
> buf.append('^');
> boolean lastStar = false;
> while (tkn.hasMoreTokens()) {
> String str = tkn.nextToken();
> switch (str) {
> case "?":
> buf.append('.');
> lastStar = false;
> break;
> case "*":
> if (!lastStar) {
> buf.append(".*");
> }
> lastStar = true;
> break;
> default:
> buf.append(Pattern.quote(str));
> lastStar = false;
> break;
> }
> }
> buf.append('$');
> return Pattern.compile(buf.toString(), Pattern.CASE_INSENSITIVE);
> }
>
> Having looked at our examples though, a lot of the common 80%+ group
> involve a block of code for each case, not a single expression. These
> would be converted to expression switches as follows;
>
> private String someMethod(String foo) {
> return switch (foo) {
> case "a", "b" -> {
> // do something
> return "AAA";
> }
> case "c", "d" -> {
> // do something else
> return "ZZZ";
> }
> default -> {
> throw new IllegalArgumentException();
> }
> }
> }
>
> The key thing to note is that the suggested syntax results in "return
> return", where you need two returns to escape the expression block. I
> can't help but feel this isn't ideal in syntax terms. (I've tried
> converting some existing code, and it looks pretty ugly).
>
> More importantly, it doesn't feel ideal in semantic terms.
> Specifically, it feels that all the new goodies in the expression form
> will result in too much of a pull to use the expression switch when
> the statement switch might actually be a better fit. ie. the code
> above would be just fine as a statement switch, if only statement
> switches had safer semantics.
>
> To summarise - having looked at our uses of switch, I think that
> adding expression switch as proposed (without enhancements to
> statement switch) might well cause migration to expression switch that
> makes code read worse, just to get the better semantics, particularly
> wrt no fall through. Really, we should be converting only 10% of our
> switch statements - the ones that genuinely should be expressions. But
> with the current proposal, we might feel compelled to convert 98% to
> get the extra safety.
>
> ** Given this situation, I think it is essential for the statement
> switch to be enhanced alongside the addition of expression switch. **
> (sorry!)
>
> I'll avoid discussing enhanced statement switch syntax, but I do think
> it would need to support all five of the new features (comma separated
> OR, case null, no fall through, enforced default, auto-default for
> enum). Given this, it would seem to me that perhaps expression switch
> should not have blocks. We pretty much never use them with lambdas.
>
>
> ---
>
> Finally, on the auto-default for enums, I think this is good. But it
> is slightly odd that enums are special. I think a case can be made
> that there should always be an auto-default that throws an exception.
> It doesn't change what developers would have to write in most cases,
> but would be more consistent.
>
> Stephen
>
>
>
> On 7 December 2017 at 22:30, Brian Goetz <brian.goetz at oracle.com> wrote:
>> In the context of Pattern Matching (JEP 305) we've explored a number of
>> improvements to `switch`, including switch expressions, expanding the range
>> of types that can be arguments to switch, better null handling, and others.
>> Many of these can be developed independently of pattern matching; we've
>> separated out a package of these features into their own JEP, as they stand
>> well on their own:
>>
>> https://bugs.openjdk.java.net/browse/JDK-8192963
>>
>>
More information about the amber-dev
mailing list