switch: using an expicit type as total is dangerous
forax at univ-mlv.fr
forax at univ-mlv.fr
Sun Aug 30 11:37:35 UTC 2020
----- Mail original -----
> De: "Brian Goetz" <brian.goetz at oracle.com>
> À: "Remi Forax" <forax at univ-mlv.fr>, "amber-spec-experts" <amber-spec-experts at openjdk.java.net>
> Envoyé: Lundi 24 Août 2020 20:57:03
> Objet: Re: switch: using an expicit type as total is dangerous
>> 2/ using an explicit type for a total type is a footgun because the semantics
>> will change if the hierarchy or the return type of a method switched upon
>> change.
>
> Sorry, I think this argument is a pure red herring. I get why this is
> one of those "scary the first time you see it" issues, but I think the
> fear has been overblown to near-panic proportions. We've spent a lot of
> time talking about it and, the more we talk, the less worried I am.
good for you,
the more i talk about it, the more i'm worried because you don't seem to understand that having the semantics that change underneath you is bad.
>
> The conditions that have to combine for this to happen are already
> individually rare:
> - a hierarchy change, combined with
> - enough use-site type inference that is not obvious what the type
> dependencies are, combined with
> - null actually being a member of the domain, combined with
> - users not realizing null is a member of the domain.
nope, you don't need a hierarchy change, changing the return type (as noticed by Tagir) and null being part of the domain is enough.
>
> Then, for it to actually be a problem, not only do all of the above have
> to happen, but an unhandled null has to actually show up.
>
> Even then, the severity of this case is low -- most likely, the NPE gets
> moved from one place to another.
nope see below
>
> Even then, the remediation cost is trivial.
for having remediation, as a user you have to first see the change of semantics, but you don't.
Ok, let's take an example, i've written a method getLiteral()
Number getLiteral(String token) {
if (token.equals("null")) {
return null; // null is part of the domain
}
try {
return Integer.parseInt(token);
} catch(NumberFormatException e) {
return Double.parseDouble(token);
}
}
and a statement switch in another package/module
switch(getLiteral(token)) {
case Integer -> System.out.println("Integer");
case Double -> System.out.println("Double");
case Number -> System.out.println("null");
}
but now i change getLiteral() to add string literal
Object getLiteral(String token) {
if (token.equals("null")) {
return null; // null is part of the domain
}
if (token.startsWith("\"") {
return token.substring(1, token.length() - 1);
}
try {
return Integer.parseInt(token);
} catch(NumberFormatException e) {
return Double.parseDouble(token);
}
}
If i only recompile getLiteral(), and run the code containing the switch, i get a ICCE at runtime because the signature of getLiteral() has changed, which is good,
but if i now recompile the switch, the code compiles without any error but with a different semantics, duh ?
Using "case var _" as the last case at least keep the same semantics, using "default Number" does not compile.
[...]
Rémi
More information about the amber-spec-experts
mailing list