tags:

views:

4233

answers:

4

Let's say I have the following class:

public class Test<E> {
    public boolean sameClassAs(Object o) {
     // TODO halp!
    }
}

How would I check that o is the same class as E?

Test<String> test = new Test<String>();
test.sameClassAs("a string"); // returns true;
test.sameClassAs(4); // returns false;

I can't change the method signature from (Object o) as I'm overridding a superclass and so don't get to choose my method signature.

I would also rather not go down the road of attempting a cast and then catching the resulting exception if it fails.

A: 

I could only make it working like this:

public class Test<E> {  

    private E e;  

    public void setE(E e) {  
        this.e = e;  
    }

    public boolean sameClassAs(Object o) {  

        return (o.getClass().equals(e.getClass()));  
    }

    public boolean sameClassAs2(Object o) {  
        return e.getClass().isInstance(o);  
    }
}
Slartibartfast
+6  A: 

The method I've always used is below. It is a pain and a bit ugly, but I haven't found a better one. You have to pass the class type through on construction, as when Generics are compiled class information is lost.

public class Test<E> {
    private Class<E> clazz;
    public Test(Class<E> clazz) {
       this.clazz = clazz;
    }
    public boolean sameClassAs(Object o) {
        return this.clazz.isInstance(o);
    }
}
Nick Fortescue
+12  A: 

An instance of Test has no information as to what E is at runtime. So, you need to pass a Class<E> to the constructor of Test.

public class Test<E> {
    private final Class<E> clazz;
    public Test(Class<E> clazz) {
        if (clazz == null) {
            throw new NullPointerException();
        }
        this.clazz = clazz;
    }
    // To make things easier on clients:
    public static <T> Test<T> create(Class<T> clazz) {
        return new Test<T>(clazz);
    }
    public boolean sameClassAs(Object o) {
        return o != null && o.getClass() == clazz;
    }
}

If you want an "instanceof" relationship, use Class.isAssignableFrom instead of the Class comparison. Note, E will need to be a non-generic type, for the same reason Test needs the Class object.

For examples in the Java API, see java.util.Collections.checkedSet and similar.

Tom Hawtin - tackline
I'd rather compare the classes with equals. Or is there a special reason to use == ?
ordnungswidrig
They are equivalent. IMO == is easier to read (although it does mean the reader has to know about the type). It's fractionally faster before inlining. Now, what I don't appear to have tested for is a null clazz.
Tom Hawtin - tackline
A: 

I was just trying to do the same thing, and one neat trick i just realized is that you can can try a cast, and if the cast fails, ClassCastException will be thrown. You can can catch that, and do whatever.

so your sameClassAs method should look like:

public boolean sameClassAs(Object o) {
    boolean same = false;
    try {
        E t = (E)o;
        same = true;
    } catch (ClassCastException e) {
        // same is false, nothing else to do
    } finally {
        return same;
    }
}
Ayman
No it won't. That is an unchecked cast. It will never throw an exception. At runtime, "t" has the type of the erasure of E, which is just Object, so there is no cast performed.
newacct
You are correct. That would save me tons of troubleshooting later, because compilation seems fine.
Ayman