why java's new feature "Pattern Matching for instanceof operator" doesn't work on List set or Map without typecasting

200 Views Asked by At

instanceof doesn't work for List even. for example

package org.practice;

import java.util.List;

public class InstanceOfDemo {

    public static void main(String[] args) {

        Employee emp1 = new Employee(1, "Ram", 10000);
        Employee emp2 = new Employee(2, "Shyam", 20000);
        Employee emp3 = new Employee(3, "Radha", 30000);

        Object obj = List.of(emp1, emp2, emp3);

        if (obj instanceof List empList) {
            for (Employee emp : empList) { //Type mismatch: cannot convert from element type Object to Employee
                System.out.println(emp.getId());
                System.out.println(emp.getName());
                System.out.println(emp.getSalary());
            }
        }
    }
}

in the above code instanceof got true and even declared empList got created but doesn't gives any advantage to the developer because in the for loop empList can't be used without type casting as for (Employee emp : (List<Employee>)empList). And if we are doing type casting only then use type casting on obj itself like traditional code then whats the use of this feature.

4

There are 4 best solutions below

1
Kayaman On

Because it turns it into a raw List due to type erasure in Java Generics, so you can only get Objects out of it.

Since instanceof doesn't work with generics (runtime vs. compile-time), you can only get the erased version of any generic class, so any FooClass<T> becomes a FooClass and you need to do an unchecked cast like in your example.

0
r-hold On

Internally, the Compiler does something called Type Erasure:

After the compiler has checked the Types, it converts List<YourType> to List<Object>. As a result, you cannot check the Types of Collections at runtime. (never ever - there is no direct way around this.)

But you can check the types of objects inside lists. Or you can create Wrapper Classes.

0
David Conrad On

You could use a second instanceof pattern match inside the loop:

if (obj instanceof List list) {
    for (Object elem : list) {
        if (elem instanceof Employee emp) {
            System.out.println(emp.getId());
            System.out.println(emp.getName());
            System.out.println(emp.getSalary());
        }
    }
}

It may be better to use a wildcard instead of a raw type:

if (obj instanceof List<?> list)
3
Reilas On

"... the for loop empList can't be used without type casting as for (Employee emp : (List<Employee>)empList). And if we are doing type casting only then use type casting on obj itself like traditional code then whats the use of this feature. ..."

This would be very convenient, I agree.

It's impossible for the compiler to infer the class of the List object's T type.
You should expect to be casting the type-parameter object here.

The instanceof keyword is useful for objects, and not any applied generic-type parameter.

I would just cast the object first; it's a fairly inexpensive operation.

if (obj instanceof List empList) {
    Employee e;
    for (Object emp : empList) {
        e = (Employee) emp;
        System.out.println(e.getId());
        System.out.println(e.getName());
        System.out.println(e.getSalary());
    }
}

Output

1
Ram
10000
2
Shyam
20000
3
Radha
30000

For reference, here is a Java documentation on "instanceof Pattern Matching".
Pattern Matching for the instanceof Operator.