Draft JEP on Primitive types in patterns, instanceof, and switch

Ron Pressler ron.pressler at oracle.com
Sat Feb 11 00:29:30 UTC 2023


You’re right that §4.10.1 is not essential to Java’s definition, but neither is it essential to the observation
that a downcast (a narrowing reference conversion) is a special case of a cast, and instanceof can be
generalised to pair nicely with more casts. I mainly brought it up in response to an email that stated categorically
that no subtyping relationship exists among primitives as a way of showing that, as Brian then articulated
better, that the way the language actually works is not necessarily equivalent with mental models of the language
that each of us forms in our minds through interaction with the language (and those models are likely different
from one person to another).

There are other casts than downcasts, and the decision that instanceof is only related to downcasts but not
other casts is just as arbitrary as the decision that it’s related to all casts, and both of them are consistent with
the existing spec.

Ultimately, the question of how instanceof is to be generalised — to which casts it relates — has different
personal aesthetic answers, none of them philosophically right or wrong. But that’s not what drives the
language decision. Rather, what drives the decision is which generalisation is most useful. Examples
that show why the proposed generalisation is useful will follow.

As to your point about not all ints being valid floats (regardless of the subtyping relationship in §4.10.1),
note that while it is a compile-time error to write `x instanceof String` when the compile-time type of `x`
is `Integer`, it is not a compile-time error to write `x instanceof List` if the type of `x` is `Serializable`, or
`x instanceof Serializable` when the type of `x` is `List`, even though not all Lists are Serializable and
not all Serializables are Lists. As an approximation, we allow instanceof when there can be any
intersection of instances between the type of the expression and the pattern.

The generalisation of instanceof to pair with more kinds of casts is consistent with the language as well
as with the mental models some of us have of it, and the relevant discussion about the feature isn’t
about how it feels but what problems it solves (and/or creates).

— Ron

On 10 Feb 2023, at 23:20, Robbe Pincket <robbepincket at live.be<mailto:robbepincket at live.be>> wrote:


This email was initially sent to amber-spec-observers by accident.



Hi all



I've long considered whether I should voice my opinion on

the matter or not, given that I felt like some of my

arguments had already been made before.



Spoiler, I'm not a fan of `instanceof byte`.



Every time I write this email, I recheck something in

the archives to check something and realize that someone

already said *something* about one of the arguments I'm

trying to make. So here is just a list of things that

have come up that I still felt like mentioning something.



---



I asked a whether the following would be legal in an earlier email.

The draft now explains that it is, but I still wonder what this snippet

would get desugared to.



```

Number num = ...;



if (num instanceof byte b) {

    ...

}

```



---



On 2023-01-27 at 12:01 UTC, Ron Pressler wrote

(as reaction to an email by Remi Forax on 2023-01-27 at 11:06 UTC):



> §4.10.1 of the JLS has stated that a subtyping relationship among primitives exists (byte <: short <: int <: long, float <: double) since at least Java 1.6 (possibly earlier).

>

>> By example, you can not override a method that returns a double with a method that returns an int because overriding is about subtyping relationship, not conversion (for the history, Neal Gafter had implemented method overriding that allow primitive conversions in javac in one of the early versions of Java 1.5 but to it was decided to not go that path).

>

> The rules for return types in overridden methods (§8.4.8.3) doesn’t refer to subtyping but to "return-type-substitutability” (§8.4.5) which, in turn, also doesn’t talk about subtyping but only about subtyping *in the case that the return type is a reference type*. So even though there is a subtyping relationship between int and long (though not between int and double), they are not return-type-substitutable, which places additional constraints beyond mere subtyping.



It seems you missed a single line in the spec. `long` is also a

direct subtype of `float` so `int` is in fact a subtype of `double`



>> I agree that revisiting concepts is something we have to do, but breaking the difference between subtyping and primitive conversions for a corner case, in isolation, is a decision hard to understand.

>

> First, a subtyping relationship for primitives does exist, though it doesn’t cover all of the possible conversions.

Second, nothing is broken, shattered, or wrecked even in cases subtyping doesn’t exist, because the draft doesn’t propose to *change* the meaning of patterns, but rather to generalise them. I.e. its insight is that subtyping is a special case of assignment conversion, and so patterns are “really” about the validity of such conversions rather than about the specific case of subtyping.



I had a quick look over the spec and I couldn't find any place that

actually used the subtyping relation between primitives. The case

you seem to be claiming widening primitive conversion matches 100%

with the rules about subtyping, it actually doesn't use subtyping

at all and just lists all possible conversions.



A interesting question that could be asked is:

"What does subtype/supertype mean". Although it is used in the spec

it isn't really explain apart from being a reflexive and transitive

relationship between types.



---



A few of the emails argue that we should see `instanceof` as a

safe cast check, not like an "inheritance" check or

a "reference subtype" check. If the operator was called

`canbesafelyconvertedto` I might agree, but thats not the case.



It feels very weird (to me) to not have an operator called

`instanceof`, not check whether the lhs is an instance of the

rhs.



Some of you might be thinking: "I guess you want to argue that

you can't have instances of an `byte` so `instanceof byte`

doesn't make sense", which isn't true. Valhalla discussions

have agreed that `byte` has instances. It has 256 different ones:

the values from -128 to 127.



Now this might seem I'm arguing in favour of `instanceof byte`.

And on it's own that might look correct, but then you have

justify the fact that the integer values/instances between -128

and 127 and the byte values/instances are the same. In my mind

the way this would be justified is by using the fact that `byte`

is a subtype of `int` which is why it's set of instances is a

subset of the instances of `int`. But this raises the issue

that the set of instances of `int` can't be a subset of the set

of instances of `float` cause not all ints are valid floats.



---



I feel like I spend too much time writing, rewriting and deleting this

email so this is it (for now).



TL; DR: I don't like `instanceof byte` and JLS chapter 4.10.1



Kind regards

Robbe Pincket

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20230211/f1c0d6ab/attachment-0001.htm>


More information about the amber-dev mailing list