Home > database >  Java instance type with generic version 8 vs 17
Java instance type with generic version 8 vs 17

Time:02-11

As per Java Generic and Collections by Maurice Naftalin, in below options-

  1. obj instanceof List
  2. obj instanceof List<?>
  3. obj instanceof List<? extends Object>

1st and 2nd is permitted but 3rd is not which works fine in java 8(version "1.8.0_321"). Option 1 and 2 compile and option 3 gives compile error. But 3rd option work fine with java 17(version "17.0.1" 2021-10-19 LTS). Could you please help me understand why its working in java 17. sample code which I am trying -

var a = List.of(2,3,4,5,23);
var b = a instanceof List<? extends Object>;
return b;

CodePudding user response:

Prior to Java 16, the only purpose of instanceof was to check whether a referenced object is assignable to the specified type and since type erasure prevents from checking whether an object truly is assignable to a parameterized type, it made no sense to allow an expression pretending that such a test was possible.

The only thing which could be tested, was whether the object is an instance of List and hence, it was only allowed to write … instanceof List or … instanceof List<?>, as neither pretends to test the list’s element type.

Starting with Java 16 (available as preview since 14), instanceof allows to declare a new variable of the specified type.

For example:

public void someMethod(Iterable<String> i) {
    if(i instanceof List<String> l) {
        if(l.isEmpty()) return;
        // optimized List processing
    } else {
        // generic iterable processing
    }
}

which is equivalent to

public void someMethod(Iterable<String> i) {
    if(i instanceof List<String>) {
        List<String> l = (List<String>)i;
        if(l.isEmpty()) return;
        // optimized List processing
    } else {
        // generic iterable processing
    }
}

For the declaration of a new variable, it would be impractical to be limited to raw types or wildcard types. Therefore, it is now allowed to specify actual type arguments, which are relevant to the implied variable declaration and assignment. Since the element type still can’t be checked, you are only allowed to specify types to which the compile-time source type could be cast without an unchecked cast.

So, the following would not be valid

Iterable<?> i = null;
boolean b = i instanceof List<String>;

because you can’t safely cast from Iterable<?> to List<String>.

In your example, var a = List.of(2,3,4,5,23); declares a as List<Integer>, hence, a instanceof List<? extends Object> is valid because List<Integer> is assignable to List<? extends Object> (even without a cast).

Of course, specifying element types which are not checked is only useful if you declare a variable like in the first example. But the Java language follows the principle of not adding extra rules just because some construct is less useful than the other. Therefore, you can now always specify actual type arguments to instanceof following the same rules, whether you declare a variable or not.

This feature is called Pattern Matching as it is considered a special case of a broader concept which Java is heading for. In JDK 17, there’s a similar feature for switch available as preview.

See also §14.30. Patterns in the Java Language Specification.

  • Related