Expression switch exception naming

Kevin Bourrillion kevinb at
Fri Mar 30 16:07:09 UTC 2018

On Fri, Mar 30, 2018 at 8:35 AM, Brian Goetz <brian.goetz at> wrote:

I think we are the heart of the disagreement.  I do not think that "someone
> added an enum constant" is necessarily a classpath insanity thing.  Its
> just as likely to be a garden-variety "you made one assumption over here
> and another over there" within the same codebase.

All I mean by "classpath insanity" is "all right, let me start by seeing
why the version I compiled against and the version I ran against are
different. Oh, how about that. If I compile against *that* jar, now I get a
compile error which I know exactly how to fix."

In fact, if my decision to go defaultless was intentional and I want to
stick with it, then note that I *can't* even fix this problem unless I do
the above first (i.e., ensure that I would get the *compile* error from
recompiling my code).

The same thing happens when I wrote a decorator for an abstract type and
then a new method is added to that supertype. When I see
AbstractMethodError, the first thing I have to do is fix my class path
situation so that I get the *compile* error, before I can even fix the
code. All the runtime error needs to do is prompt me to do that and then I
can take it from there normally.

The reasons we're trying to treat that case as different from this one seem
too subtle to me.

(And, we've told people for years that its OK to add enum constants.)

Yes, but we are deciding to reverse that decision 15 years later. We're
doing that because we think it's worth it, but let's at least be clear
about the fact that that is what we're doing.

> I think its at least as likely that this is in the same category as any
> other assumption we make about code we call.  Like "this method never
> returns null".  Or "this method says it returns Object, but I know it
> always returns String, so I'll cast to String."

Those situations (which suck, to be sure) I have no compile-time protection
from at all, so argument I've been making doesn't apply. (Oops, I swear
Liam and I are not coordinating our responses. :-))

So, it seems that the purpose of this exception is not to cast blame on the
> classpath, but to pinpoint the erroneous assumption.

To repeat, "casting blame" on the classpath should help the user resolve
the problem more quickly.

>   Maybe Y shouldn't have gotten new enum constants.  Maybe X made bad
> assumptions about the future maintenance trajectory of Y.  Either way, X
> and Y have to get together and get their story straight.  So the exception
> should broker that meeting.  And its seems at least as likely as not that X
> and Y are the same maintainer.
> If you got a stack trace that said:
>     UnexpectedEnumValueException: surprising enum constant
> TrafficLight.BLUE; expected one of RED, YELLOW, GREEN
>         at: MyClass.switchOnTrafficLightColors (line 666)
> or
>     UnexpectedSealedTypeMemberException: surprising subtype Blue of
> sealed type TrafficLightColors, expected one of Red, Yellow, Green
> doesn't that meet your requirement?  You look at the exception, and it
> says that a value was found that violated an assumption in your code.  It
> could either be the fault of the enum maintainer or of the switch
> maintainer (who might well be the same person.)
> On 3/30/2018 11:09 AM, Kevin Bourrillion wrote:
> I think my overarching point is still this one:
> "Today, an experienced developer knows that there is a category of Errors
> that, when you see them in the absence of reflection, always implicate this
> kind of classpath issue. I can't see why this would not belong in that same
> category."
> The distinction, when a stack trace has just ruined my day, of whether I
> need to start thinking hard about what *real* mistake I might have made *in
> my code, *or whether I probably just have Class Path Insanity I should
> check out first, seems to be like a very high order distinction - more
> useful to illuminate than other various distinctions we can make.
> On Wed, Mar 28, 2018 at 12:48 PM, Brian Goetz <brian.goetz at>
> wrote:
>> I have been figuring that if the client *has* a reasonable way to handle
>> unknown values then it will probably go ahead and do that (with a
>> `default`).
>> I think that's a fair assumption for your codebase, but not in general.
>> Developers will surely do this:
>>     x = switch (trafficLight) {
>>         case RED -> ...
>>         case YELLOW -> ...
>>         case GREEN -> ...
>>     }
>> and leave out a default because they can.  So they get a default default,
>> one that throws.  No problem.
>> The only question here is: what to throw.  My argument is that Error is
>> just too strong an indicator.  (It's like using fatal as your logging level
>> for everything; it would be more useful to use warning for things that
>> aren't fatal).
>> From the Error doc:
>> An Error is a subclass of Throwable that indicates serious problems that
>> a reasonable application should not try to catch. Most such errors are
>> abnormal conditions.
>> Serious problems mean that underlying VM mechanism have failed.
>> Encountering an unexpected input is not in this category.  Sure, it
>> deserves an exception, but its not an ICCE.
>> Therefore I assumed that what we're talking about in this conversation is
>> the* other* kind, where there is nothing safe they can do - for example
>> if I wrote a method that displays a time interval as "10 ns" or "20 s", I
>> may not find it acceptable for me to start displaying "30 <unknown unit>"
>> once I get handed TimeUnit.DAYS. My code is broken either way. If a
>> constant is added, I need to react to that, just like I do with a new
>> interface method. What does it really mean to say that this client "brings
>> a piece of the responsibility" if it doesn't really have a choice?
>> It's not unlike this:
>>     AnEnum e = f(...);
>>     switch (e) {
>>         ...
>>     }
>> and not being prepared for a null.  You'll get an NPE.  The local code
>> isn't expected to deal with it, but somewhere up the stack, someone is
>> prepared to deal with it, discard the offending incoming work item, log
>> what happened, and re-enter the work loop.
>> So, I'm not quite yet following why the binary/source compatibility
>> distinction, or the opt-in distinction, really makes all the difference
>> here.
>> Some incompatibilities are more of a fire drill than others.  Binary
>> incompatibilities (e.g., removing a method) are harder to recover from than
>> unexpected inputs.  Further, while there may be no good _local_ recover for
>> an unexpected input, there often is a reasonable global recovery.  Error
>> means "fire drill".  I claim this doesn't rise to the level of Error; it's
>> more like NumberFormatException or NPE or ClassCastException.
> --
> Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at

Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <>

More information about the amber-spec-experts mailing list