Error "illegal generic type for instanceof" when using local classes

JavaGenericsLocal Class

Java Problem Overview


I have the following Java code that uses a local class.

import java.util.Arrays;

public class X<T> {
    void m() {
        class Z {}

        for (Object o : Arrays.asList(1, 2, 3))
            if (o instanceof Z) {}
    }
}

It does not compile with the following error message:

X.java:8: error: illegal generic type for instanceof
            if (o instanceof Z) {}
                             ^
1 error

I understand that the local class Z inherits the generic type signature of X<T>, being an inner class. The same kind of compilation error appears in this example, where Z is not local, but still inner:

import java.util.Arrays;

public class X<T> {
    class Z {}

    void m() {
        for (Object o : Arrays.asList(1, 2, 3))
            if (o instanceof Z) {} // Compilation error
    }
}

It can be worked around either by making Z non-inner / static:

import java.util.Arrays;

public class X<T> {
    static class Z {}

    void m() {
        for (Object o : Arrays.asList(1, 2, 3))
            if (o instanceof Z) {} // Compiles now
    }
}

Or by qualifying X.Z:

import java.util.Arrays;

public class X<T> {
    class Z {}

    void m() {
        for (Object o : Arrays.asList(1, 2, 3)) {
            if (o instanceof X.Z) {}    // Compiles now
            if (o instanceof X<?>.Z) {} // Also
        }
    }
}

But how can I qualify a local class, or work around this limitation, without changing the local class itself?

Java Solutions


Solution 1 - Java

To me this seems to be an oversight or limitation in the Java language and I do not think it is possible.

The referenced type in an instanceof expression must be reifiable according to JLS 4.7, meaning that it must be expressed as a reifiable type by its fully qualified name. At the same time, JLS 6.7 states that local classes do not have a fully qualified name, they can therefore not be expressed as reifiable.

If you declare Z as generic, the instanceof operator treats Z as a raw type where all generic properties to it - in this case the enclosing class - are considered raw as well. (Similar to a generic methods of a raw type being considered as raw despite any generic signature. This is a measure to retain backwards compatiblity of type generification.) Since any raw type is reifiable, declaring Z to be generic will compile.

Solution 2 - Java

A possible workaround is to use reflection:

import java.util.Arrays;

public class X<T> {
    void m() {
        class Z {}

        for (Object o : Arrays.asList(1, 2, 3))
            if (Z.class.isInstance(o)) {}
    }
}

Solution 3 - Java

Apparently, by making Z generic compilation succeeds. I expected that to require <T> as the type parameter, but you just have to make it generic, so anything will do

import java.util.Arrays;

public class X<T> {
    void m() {
        class Z<Anything> {}

        for (Object o : Arrays.asList(1, 2, 3))
            if (Z.class.isInstance(o)) {}
    }
}

Proper solution would be qualify the local class, but I don't think you can. Either you refactor it to a private static class or that's probably the best you can get.

Solution 4 - Java

This should work either. Using reflection too. But seems a valid solution.

import java.util.Arrays;

public class X<T> {


    void m() {

        class Z2 {
        }
        
        for(Object o: Arrays.asList(1,2,3)) {
            if(Z2.class.isAssignableFrom(o.getClass())) {

            }
        }

    }

}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionLukas EderView Question on Stackoverflow
Solution 1 - JavaRafael WinterhalterView Answer on Stackoverflow
Solution 2 - JavaLukas EderView Answer on Stackoverflow
Solution 3 - JavaEdoardo VacchiView Answer on Stackoverflow
Solution 4 - JavaJWThewesView Answer on Stackoverflow