<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body style="overflow-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;">
Hi Matthias,
<div><br>
</div>
<div>Just circling back to this now we have support for qualified enum constant labels in JEP 441 [1].</div>
<div><br>
</div>
<div>I’m happy to say that the “single, flattened switch” code now works as expected:</div>
<div><br>
</div>
<div>
<div><font face="Menlo">$ cat EnumTest.java</font></div>
<div><font face="Menlo"><br>
</font></div>
<div><font face="Menlo">class EnumTest {</font></div>
<div><font face="Menlo"><br>
</font></div>
<div><font face="Menlo">    sealed interface Token {}</font></div>
<div><font face="Menlo">    enum Digit implements Token { ONE, TWO, THREE }</font></div>
<div><font face="Menlo">    enum Letter implements Token { A, B, C }</font></div>
<div><font face="Menlo"><br>
</font></div>
<div><font face="Menlo">    static String toStringFlat(Token token) {</font></div>
<div><font face="Menlo">       return switch (token) {</font></div>
<div><font face="Menlo">            case Digit.ONE -> "1";</font></div>
<div><font face="Menlo">            case Digit.TWO -> "2";</font></div>
<div><font face="Menlo">            case Digit.THREE -> "3";</font></div>
<div><font face="Menlo">            case Letter.A -> "A";</font></div>
<div><font face="Menlo">            case Letter.B -> "B";</font></div>
<div><font face="Menlo">            case Letter.C -> "C";</font></div>
<div><font face="Menlo">        };</font></div>
<div><font face="Menlo">    }</font></div>
<div><font face="Menlo"><br>
</font></div>
<div><font face="Menlo">    public static void main(String[] args) {</font></div>
<div><font face="Menlo">        System.out.println(toStringFlat(Digit.TWO));</font></div>
<div><font face="Menlo">        System.out.println(toStringFlat(Letter.C));</font></div>
<div><font face="Menlo">        System.out.println("End");</font></div>
<div><font face="Menlo">    }</font></div>
<div><font face="Menlo"><br>
</font></div>
<div><font face="Menlo">}</font></div>
<div><font face="Menlo"><br>
</font></div>
<div><font face="Menlo">$ build/macosx-x86_64-server-release/images/jdk/bin/java EnumTest.java</font></div>
<div><font face="Menlo">2</font></div>
<div><font face="Menlo">C</font></div>
<div><font face="Menlo">End</font></div>
<div><br>
</div>
</div>
<div>
<div>Thanks for the feedback,</div>
<div>Gavin</div>
<div><br>
</div>
<div>[1] Pattern matching for switch: <a href="https://openjdk.org/jeps/441">https://openjdk.org/jeps/441</a></div>
<div><br>
<blockquote type="cite">
<div>On 24 Nov 2021, at 08:23, Matthias Perktold - ASA <matthias.perktold@asaon.com> wrote:</div>
<br class="Apple-interchange-newline">
<div>
<div>I've played around with sealed interfaces implemented by multiple enums only, and I've found some inconveniences I wanted to share here.<br>
<br>
Consider the following class hierarchy:<br>
<br>
   sealed interface Token {<br>
       enum Digit implements Token { ONE, TWO, THREE }<br>
       enum Letter implements Token { A, B, C }<br>
   }<br>
<br>
I can use a nested switch expression to cover all cases:<br>
<br>
   static String toStringNested(Token token) {<br>
       return switch (token) {<br>
           case Digit d -> switch (d) {<br>
               case ONE -> "1";<br>
               case TWO -> "2";<br>
               case THREE -> "3";<br>
           };<br>
           case Letter l -> switch (l) {<br>
               case A -> "A";<br>
               case B -> "B";<br>
               case C -> "C";<br>
           };<br>
       };<br>
   }<br>
<br>
I thought that in theory, I should be able to use a single, flattened switch that covers all digits and all letters:<br>
<br>
   static String toStringFlat(Token token) {<br>
       return switch (token) {<br>
           case ONE -> "1";<br>
           case TWO -> "2";<br>
           case THREE -> "3";<br>
           case A -> "A";<br>
           case B -> "B";<br>
           case C -> "C";<br>
       };<br>
   }<br>
<br>
This is probably deliberately not supported (yet). Anyway, here are my findings:<br>
<br>
1. The compiler requires that the enum constants be unqualified.<br>
But since we have multiple enums here, just leaving the enum away doesn't work either; we need to add static imports:<br>
<br>
   import static package.to.Token.Digit.*;<br>
   import static package.to.Token.Letter.*;<br>
<br>
2. The compiler cannot figure out that the switch is exhaustive, even if we do cover all cases.<br>
We need to add a default case to fix this compiler error.<br>
<br>
3. With the default case added, the code still doesn't compile (my IDE thinks it does);<br>
We get an error "constant expression required" for each case.<br>
<br>
<br>
In case you wonder why one would use sealed hierarchies of enums, it probably makes more sense to think about these<br>
as "enums with subgroups". Using this approach, you can model more precisely that a method accepts or returns only a subset<br>
of the cases, but a logical coherent subset.<br>
One special reason of grouping enum constants is the use of generics: we could extend our hierarchy as follows:<br>
<br>
   sealed interface Token<T> {<br>
       T value();<br>
       enum Digit implements Token<Integer> {<br>
           ONE, TWO, THREE;<br>
           @Override public Integer value() { ... }<br>
       }<br>
       enum Letter implements Token<Character> {<br>
           A, B, C;<br>
           @Override public Character value() { ... }<br>
       }<br>
   }<br>
<br>
This could also be seen as a workaround for JEP 301: "Enhanced Enums", but a very good one in my opinion.<br>
Only when every enum constant needs a different type argument (as in the JEP's examples), you would need to wrap each constant<br>
in its own enum.<br>
<br>
Overall, I think there are good places for such groupings of enums. And it would help if they could be used just like a single enum inside<br>
switch.<br>
<br>
<br>
Cheers,<br>
Matthias Perktold<br>
<br>
</div>
</div>
</blockquote>
</div>
<br>
</div>
</body>
</html>