Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JAVA 17 : Pattern matching subtype

I'm using Java 17, the last line shouldnt compile, but when i'm using eclipse it does compile, now i'm confused should the value be a subtype or it can be anything?

Double value = 123.3;
if(value instanceof Double ) {}
if(value instanceof Double data) {} // DOES NOT COMPILE
like image 490
Hamza Avatar asked Sep 06 '25 00:09

Hamza


2 Answers

JLS-15.20.2 explicitly says that this is not allowed:

If the type of the RelationalExpression is a subtype of the type of the Pattern, then a compile-time error occurs

Regarding the meaning of "subtype", JLS-4.10 states:

The supertypes of a type are obtained by reflexive and transitive closure over the direct supertype relation, written S >1 T, which is defined by rules given later in this section. We write S :> T to indicate that the supertype relation holds between S and T.

S is a proper supertype of T, written S > T, if S :> T and S ≠ T.

The subtypes of a type T are all types U such that T is a supertype of U, and the null type. We write T <: S to indicate that that the subtype relation holds between types T and S.

T is a proper subtype of S, written T < S, if T <: S and S ≠ T.

From the wording it is clear that a type T must be a subtype (and supertype) of itself. If T cannot be a subtype (supertype) of itself it would be unnecessary to define the terms "proper subtype" and "proper supertype"


The reason for this decision is documented in JEP-394:

Make it a compile-time error for a pattern instanceof expression to compare an expression of type S against a pattern of type T, where S is a subtype of T. (This instanceof expression will always succeed and is then pointless. The opposite case, where a pattern match will always fail, is already a compile-time error.)


Note that (as Holger has pointed out) the upcoming version Java 21 will drop the requirement that RelationalExpression must not be a subtype of the type of the Pattern. That means that with Java 21 your code will compile.

like image 53
Thomas Kläger Avatar answered Sep 07 '25 19:09

Thomas Kläger


That syntax is part of a relatively new feature (JDK 16) in Java which they call pattern matching and is detailed in JEP 394.

The example is trivial if the runtime type of the variable equals its declared type. So let's pretend we instead had a

Object value;

whose runtime type happened to be Double.

if (value instanceof Double) {
  ...
}

The inside of this if statement will run if value is a Double at runtime. However, the compile-time type of that variable is still Object, which led to a very common (and redundant) paradigm.

if (value instanceof Double) {
  Double data = (Double)value; // This cast ALWAYS succeeds
  ...
}

Since, after checking the type, we probably want to use the variable as that type.

So with JEP 394, we can write

if (value instanceof Double data) {
  ...
}

which does the typecheck and the cast in one go. On top of that, the variable is bound immediately, and is available in later conjunctive clauses.

if ((value instanceof Double data) && (data.doubleValue() > 0.0)) {
  ...
}

This works since data is a Double, not an Object. As opposed to the more verbose pre-pattern-matching syntax

if ((value instanceof Double) && (((Double)value).doubleValue() > 0.0)) {
  ...
}

or

if (value instanceof Double) {
  Double data = (Double)value;
  if (data.doubleValue() > 0.0) {
    ...
  }
}

In your particular case, since both the declared and runtime type are Double, the extra variable doesn't do you any favors. It's a degenerate case where the pattern matching syntax isn't useful. But it's still allowed. Depending on compiler, you might get a warning about this.

like image 28
Silvio Mayolo Avatar answered Sep 07 '25 21:09

Silvio Mayolo