Submission: switch (...) instanceof feature

Reinier Zwitserloot reinier at zwitserloot.com
Tue Mar 31 05:50:58 PDT 2009


Nice proposal, Jeroen.

I would make the case (heh) for using 'case null' instead. null's type  
is not 'void' - it's every type. Technically, the first case FOO:  
should match an input null, so you're looking at an exceptional  
situation already. This exceptionality is also built into the  
'instanceof' keyword, eventhough technically 'null instanceof FOO' is  
true for all FOO, instanceof specifically defines:

If the LHS is null, then instanceof will evaluate to false, eventhough  
null is technically an instance of all types.

Such a special rule seems inevitable in a switch() instanceof  
construct as well, so roll with it.

If the switch statement matches stictly top-down first-match, then you  
should add that it is a compiler error to relist any type, or to  
relist any subtype of any other type. Thus:

switch (foo) instanceof {
case List:
case ArrayList:
}

should be a compile-time error, because the second case block couldn't  
possibly ever be matched (any qualifiying object would already match  
'List' first!)


fallthrough sucks and was a clear mistake in the original java, but  
defining switch()instanceof to not fall through, but switch() to yes  
fall through, is an even greater mistake. If avoiding fall through is  
that important, then the syntax should change to require braces:

switch (foo) instanceof {
    case (String) {
      /* code */
    } case (Number) {
     /* code */
    }
}


and then you still have an inconsistency in the JLS which is  
unfortunate, though at least it would no longer be as confusing.

I agree that fallthrough precludes automatic casting. 'break' should  
therefore either be mandatory, or only mandatory if a future block  
will be using the variable name, -or- the type of the variable name  
inside any case construct is the intersection type of all cases  
(including itself) above it that do not have a definite exit, up to  
the nearest case statement that does have a definite exit. Something  
like this could be both useful and easily made legal:

Object foo = foo();

switch (foo) instanceof {
case Double:
case Float:
  //foo, as an expression, is now a Number.
  break;
case ArrayList:
case LinkedList:
  //foo, as an expression, is now:
  //AbstractList<RAW> & Serializable & Cloneable
  default:
  case String:
   //foo is still just Object due to the 'default'.
}


regarding unchecked warnings: Major point of this proposal. One way  
around it is to notice that if the variable name is never actually  
used inside / the intersection type doesn't include any generics, raw  
or otherwise, no warning is needed. This is one of the main reasons  
why I would push for lub on the various involved types for any case  
block. The feel would then be: The type is the type of the expression,  
with some magic voodoo applied to make that type more specific, but  
only with added specific types for things where the compiler is 100%  
certain it'll work out that way. In this view, adding Map<String,  
Object>: to the case list would actually not be legal, because the  
compiler can't ensure those generics. You'd only get the raw types  
(which is a compiler certainty - the uncertainty starts when getting  
or putting stuff in that).

  --Reinier Zwitserloot



On Mar 29, 2009, at 22:43, Jeroen van Maanen wrote:

> Artur Biesiadowski wrote:
>> 1) Why case void: instead of case null: ?
>
> Yes, I agree that it is potentially confusing. At the other hand,  
> null is an instance not a type. It would also be possible to add a  
> type or even a keyword to describe the type of null, but that would  
> increase the impact of the change and it would mean another type  
> with no instances. I thought that void would pretty much fit the  
> descrition of what I would like: a keyword that indicates a type  
> that would not have any actual instances. I suppose that if we are  
> stretching the exact meaning of the terms anyway, then 'case null:'  
> would probably just be as well and easier to remember.
>
>> 2) What is a expected behavior in case of multiple matches ? For  
>> example
>
> I propose to do strict top-down first-match-only evaluation, because  
> if the given variable matches many of the labels and the types of  
> the labes belong to a complicated hierarchy of interfaces, then  
> reordering the cases according to inheritance could lead to  
> unpredictable results. I don't think that executing all matching  
> blocks would be a good idea, because I think that many code readers  
> would not expect this from a switch statement. Although it would be  
> possible to allow a fallthrough from a specific type to a super  
> type, I am afraid that allowing such highly constrained  
> constructions would lead to code that is hard to read and easy to  
> misunderstand.
>
>> switch(collection) instanceof {
>> case RandomAccess:
>>   ...
>> case List:
>>  ...
>> case Collection:
>> ...
>> }
>> Do I need to put switch cases in correct order because of top-to- 
>> down evaluation or most specific one will be used? Or maybe all  
>> matching ones ?
>> 3) I think you have forgotten to put 'break' in your examples,  
>> unless you plan to disallow fall-through.
>
> Ah, I should have made that explicit. No breaks necessary and no  
> lists of labels before a statement block. I could not think of a way  
> to get the  casts right in a fallthrough situation. Maybe it would  
> be less confusing to make break a compulsory statement at the end of  
> each statement block. That is:
>
> TypeSwitchBlockStatementGroup:
>   TypeSwitchLabel BlockStatements break ;
>
> What do you think?
>
>> Regards,
>> Artur Biesiadowski
>
> I just thought about another situation that needs to be clarified:
>
> 4) How does the switch instanceof statement handle generics?
>
> I would like to see
>
> switch (model) instanceof {
> FreeMarkerModel:
> template.process(..., model);
> Map:
> template.process(..., toFMM(model))
> }
>
> translated as
>
> if (model instanceof FreeMarkerModel) {
> FreeMarkerModel model$1 = (FreeMarkerModel) model;
> template.process(..., model$1);
> }
> else if (model instanceof Map<?,?>) {
> Map<?,?> model$2 = (Map<?,?>) model;
> template.process(..., toFMM(model$2));
> }
>
> So each type parameter that we cannot guess would be specified as ?.
>
> The following would be illegal, because the specification allows no  
> type parameters after the type name in a label.
>
> switch (model) instanceof {
> ...
> Map<String,Object>:
> ...
> }
>
> This is fine, because I can't think of a way where the desugared  
> code would be free of "unchecked" warnings.
>
> Regards,  Jeroen
>
>




More information about the coin-dev mailing list