[pattern-switch] Exhaustiveness

Guy Steele guy.steele at oracle.com
Fri Aug 21 01:02:02 UTC 2020



> On Aug 20, 2020, at 6:14 PM, Brian Goetz <brian.goetz at oracle.com> wrote:
> 
> I suspect there are other orderings too, such as "any nulls beat any novels" or vice versa, which would also be deterministic and potentially more natural to the user.  But before we go there, I want to make sure we have something where users can understand the exceptions that are thrown without too much head-scratching.  
> 
> If a user had:
> 
>     case Box(Head)
>     case Box(Tail)
> 
> and a Box(null) arrived unexpectedly at the switch, would NPE really be what they expect?  An NPE happens when you _dereference_ a null.  But no one is deferencing anything here; it's just that Box(null) fell into that middle space of "well, you didn't really cover it, but it's such a silly case that I didn't want to make you cover it either, but here we are and we have to do something."  So maybe want some sort of SillyCaseException (perhaps with a less silly name) for     at least the null residue.  

I believe that if Head and Tail exhaustively cover an enum or sealed type (as was the intended implication of my example)—more generally, in a situation that is optimistically total---then the user would be very happy to have some sort of error signaled if some other value shows up unexpectedly in a statement switch, whether that value is “Ankle" or “null”.  Maybe a new error name would be appropriate, such as UnexpectedNull.

If the user does not want such implicit handling of an optimistically total situation in a statement switch, then it is always possible to provide explicit clauses “case null: break;” and “default: break;”.

> On the other hand, ICCE for Box(novel) does seem reasonable because the world really has changed in an incompatible way since the user wrote the code, and they probably do want to be alerted to the fact that their code is out of sync with the world. 

Yep.

> Separately (but not really separately), I'd like to refine my claim that `switch` is null-hostile.  In reality, `switch` NPEs on null in three cases: a null enum, String, or primitive box.  And, in each of these cases, it NPEs because (the implementation) really does dereference the target!  For a `String`, it calls `hashCode()`.  For an `enum`, it calls `ordinal()`.  And for a box, it calls `xxxValue()`.  It is _those_ methods that NPE, not the switch.  (Yes, we could have designed it so that the implementation did a null check before calling those things.)  



More information about the amber-spec-experts mailing list