tags:

views:

53

answers:

2

Given:

public class C<T> {
    private class D {
        public boolean equals( Object o ) {
            if ( !(o instanceof D) )    // line 4
                return false;
            D other = (D)o;             // line 6
            return i == other.i;
        }
        int i;
    }
}

I get:

C.java:4: illegal generic type for instanceof
          if ( !(o instanceof D) )
                              ^

I also get an "unchecked cast" warning about line 6. Why? The o is not a generic type -- it's just a plain Object. How can I correctly implement equals() by both checking for and casting to an instance of D?

Note: Obviously, this code example is a whittled-down version of my actual code. The real classes for C and D are much larger and D is a private inner class of C used by its implementation.

FYI: The real D does make use of the generic parameter T.

+2  A: 

If you make the inner class static, the code compiles fine.

Ex:

private static class D{...}

Read here for the difference.

You could also try o.getClass() != D.class (after guarding against o being null, of course)

Stefan Kendall
I know the difference between non-static and static nested classes. In this case, I need it non-static. If I make the nested class static, I can't make use of the generic parameter T.
Paul J. Lucas
What about o.getClass() and D.class comparisons? You won't know that they're of the same parameterized type, but I'm not sure you can reliably do that anyway with type erasure.
Stefan Kendall
@Stefan: What type erasure? 'o' is of type Object directly.
Paul J. Lucas
Supposing 'o' is pointing at some type D<K>, there's no way to tell that it's a D<K> and not a D<L>. So if I'm a D<L> and o is really a D<K>, the best you can say is that they're both a D.
Stefan Kendall
@Stefan: But it's a plain Object which could be *any* object: D<K>, Integer, Runnable, etc. I don't understand why the type matters.
Paul J. Lucas
+3  A: 

The o is not a generic type -- it's just a plain Object.

That's not the problem. The problem ... and the root cause of both compilation errors ... is that D is a generic class. And it is generic because it is a non-static nested class in a generic class. Its fully qualified name would be some.pkg.C<T>.D.

FYI: The real D does make use of the generic parameter T.

And the fact that it could make use is what makes D a generic class.

The reason that you cannot use instanceof D or (D) is generic type erasure. Basically, the runtime cannot distinguish between the types of (say) C<String>.D and `C<Integer>.D. And since it cannot do that, it cannot determine if instanceof D should return true or false or if (D) should succeed or throw ClassCastException.

One solution would be to declare D as static. But that will work with your "real D" because a static class cannot make use of a generic type parameter from the enclosing class(es).

Another solution is to instantiate the outer class C passing it the actual type of T as a java.lang.Class<T> instance. Then use this Class instance to implement the runtime type checks and casts as required. This is likely to be messy.

The third solution is to carefully analyze the code and determine if it is safe to a @SuppressWarning annotations to suppress the "unsafe cast" etc warnings.

What type erasure? 'o' is of type Object directly.

Actually Object is the declared type of the o variable. The actual object will most likely have some other type, and it is that type that (if it is a D instance for example) will have been subjected to type erasure.

Stephen C