Home > Enterprise >  Combining assert statement and instanceof pattern matching
Combining assert statement and instanceof pattern matching

Time:11-24

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).

  • Related