In Java's instanceof pattern matching, an additional identifier is used to narrow the type:
Object o = "test";
if (o instanceof String s) {
System.out.println(s.length()); // s is of type String
}
Why is that necessary? The JEP doesn't seem to justify it, but I'm sure there's a reason. What's undesirable about the following?
Object o = "test";
if (o instanceof String) {
System.out.println(o.length());
}
TypeScript and Kotlin do similar things without another identifier.
I'm guessing it's a backwards-compatibility thing - Java's quirk's usually are - but I can't think of an example which would demonstrate that.
The only thing I can think is that someone may have written something similar to that second example pre-pattern matching and relies on it to not compile, but that seems like a weak justification.
If Java introduced something like a “smart instanceof”, we could argue that this feature could have worked without introducing a new variable.
But that’s not what has been introduced. The new feature is Pattern Matching, a far bigger concept, though only implemented in the simplest form in the first step. This is also a new integration approach, instead of working decades on the big feature, smaller features are continuously added to Java while maintaining the big vision.
JDK-8260244 describes one of the next steps, which would allow something like
(given)
record Point(int x, int y) {}
void printSum(Object o) {
if (o instanceof Point(int x, int y)) {
System.out.println(x+y);
}
}
or even
(given)
record Point(int x, int y) {}
enum Color { RED, GREEN, BLUE }
record ColoredPoint(Point p, Color c) {}
record Rectangle(ColoredPoint upperLeft, ColoredPoint lowerRight) {}
static void printXCoordOfUpperLeftPointWithPatterns(Rectangle r) {
if (r instanceof Rectangle(ColoredPoint(Point(var x, var y), var c), var lr)) {
System.out.println("Upper-left corner: " + x);
}
}
Since Pattern Matching includes the creation of (sometimes multiple) new variables, the case of creating only one new variable containing the same reference as an already existing variable is just a corner case. Even what has been implemented so far, covers more cases than that. E.g.
if(foo.getObject() instanceof List<?> l && l.get(0) instanceof Map<?,?> m
&& m.get("foo") instanceof String s) {
// use s
}
It’s also worth noting that the scope of a pattern variable is complex. It would be even more confusing, if the variable existed outside this scope as well, but with a different type.
So, changing the type of a variable contextually in the presence of instanceof
, while tempting, would cause problems (some comments also mentioned method overload selection), while at the same time, wouldn’t fit into the actual vision of the Java language developers.