In Java 19, I'm trying to use instanceof
pattern matching inside an assert
statement.
I would expect the matched type would be available after the statement, but the Java compiler does not recognize the new variable.
We create a variable Object obj
, and we cannot know whether it contains an Integer
or a String
.
We pass the variable to two test methods.
public class AssertPatternMatching {
public static void main(String[] args) {
Object obj = args.length == 0 ? Integer.valueOf(42) : "Hello";
afterAssert(obj);
insideMessage(obj);
}
In method afterAssert()
, we assert that obj
is a String
, and bind the string to the new variable str
via instanceof
pattern matching.
I would expect str
to be known and usable in the next line.
However, the compiler does not know symbol str
.
private static void afterAssert(Object obj) {
assert obj instanceof String str;
str.length(); // javac: cannot find symbol variable str
}
In method insideMessage()
, we use a convoluted assert
statement to check that obj
is not a String
. If it would be, the assertion fails, and we can provide a message.
As the instanceof
check is negated, pattern matched str
variable should be available for the error message.
However, the compiler again does not know symbol str
.
private static void insideMessage(Object obj) {
assert !(obj instanceof String str) : "Is a string: " str.length();
// line above: javac: cannot find symbol variable str
obj.hashCode();
}
It works if we replace assert
statements with if
:
private static void afterAssertIf(Object obj) {
if(obj instanceof String str) {
str.length();
} else {
throw new AssertionError();
}
}
private static void insideMessageIf(Object obj) {
if (!(obj instanceof String str)) {
obj.hashCode();
} else {
throw new AssertionError("Is a string: " str.length());
}
}
The insideMessage() example is really an edge case, so I understand it's not supported. I would have expected afterAssert() to work, however. Is this a deliberate design choice or a bug? If deliberate, what's the rationale?
Complete code at https://gist.github.com/enikao/57bb1b10ce3126494ec4baa2bc7db2df
CodePudding user response:
assert
statements are only executed when assertions are enabled with -ea
. Without -ea
, the code is not executed, therefore the variable is never assigned/does not exist (this is the default).
private static void afterAssert(Object obj) {
assert obj instanceof String;
((String) str).length();
}
Because the compiler cannot know if the assert
statement will be executed, it cannot rely on variables declared within the statement to be available outside of the statement (because the statement might never have been executed).