views:

332

answers:

5
+5  Q: 

Java generics

I'd like to implement a method that takes an Object as argument, casts it to an arbitrary type, and if that fails returns null. Here's what I have so far:

public static void main(String[] args) {
    MyClass a, b;
    a = Main.<MyClass>staticCast(new String("B"));
}

public static class MyClass {
}

public static <T> T staticCast(Object arg) {
    try {
        if (arg == null) return null;
        T result = (T) arg;
        return result;
    } catch (Throwable e) {
        return null;
    }
}

Unfortunately the class cast exception is never thrown/caught in the body of the staticCast() function. It seems Java compiler generates the function String staticCast(Object arg) in which you have a line String result = (String) arg; even though I explicitly say that the template type should be MyClass. Any help? Thanks.

+6  A: 

You cannot do what you want... at least not this way. The compiler removed all of the information so you wind up with (T) being Object in the cast - and that works.

The problem is that later on you are doing MyClass = (String)object; and that isn't allowed.

You have bypassed the compilers checking and made it fail at runtime.

So what is it exactly that you are trying to do? If you tell us why you want to do this we can probably tell you the Java way of doing it.

Given your comment you might try something like:

public class Main
{
    public static void main(String[] args)
    {
        String a;
        MyClass b;
        a = staticCast(new String("B"), String.class);
        b = staticCast(new String("B"), MyClass.class);
        System.out.println(a);
    }

    public static class MyClass
    {
    }

    public static <T> T staticCast(Object arg, final Class clazz)
    {
        // edit - oops forgot to deal with null...
        if(arg == null)
        {
            return (null);
        }

        // the call to Class.cast, as mmeyer did, might be better... probably is better... 
        if(arg.getClass() != clazz)
        {
            // Given your answer to Ben S...
            return (null);

            // throw new ClassCastException("cannot cast a " + arg.getClass() + " to a " + clazz);
        }

        return ((T)arg);
    }
}
TofuBeer
I see, so T is an object in the cast. I want a separate method for each T that will cast the argument to (T), and if that fails an exception is caught and not thrown. According to Paul Tomblin (below post) that's not going to happen.
Budric
the code I provided in the edit should do what you want
TofuBeer
+1  A: 

Don't mistake Java generics for C++ templates. There is only going to be one staticCast method that's called with different type erasure, not one for each T.

Not using Generics at all, I would write it this way:

public static void main(String[] args) {
    MyClass a, b;
    a = (MyClass)Main.staticCast(new String("B"), MyClass.class);
}

public static class MyClass {
}

public static staticCast(Object arg, Class class) {
        if (arg != null && class.isAssignableFrom(arg))
          return arg;
        return null;
}

Or stealing from another answer, using generics:

public static <T> T staticCast(Object arg, Class<T> class) {
        if (arg != null && class.isAssignableFrom(arg))
        {
           T result = class.cast(arg);
           return result;
        }
        return null;
}
Paul Tomblin
I forgot that CCE was a RuntimeException and didn't require a catch block, or I'd have done it like your second method originally.
Michael Myers
Can you elaborate why Java compiler doesn't generate the function MyClass staticCast(Object arg); when I explicitly specify the type T in the line: a = Main.<MyClass>staticCast(new String("B")); I explicitly told it to...I thoughtA link to some reading would be great.
Budric
@Budric: Try http://java.sun.com/docs/books/tutorial/java/generics/erasure.html. Also, search for questions tagged [java] [generics]. There are some pretty interesting ones around.
Michael Myers
isAssignableFrom should be isInstance, and no need to check for null.
Tom Hawtin - tackline
Correct me if I'm wrong, but I thought isAssignableFrom will find subclasses of the given classes, not just ones that are exactly that type?
Paul Tomblin
+4  A: 

You mean like Class<T>.cast(Object obj)?

Something like:

Class.forName("YourClass").cast(obj);

Should do pretty much what you want.

Be aware that this is quite smelly though and is probably a sign of poor design.

Ben S
Thanks. The problem is this will throw class cast exception if it fails. It's identical to the code: (YourClass) object and you get an exception thrown on error which you have to check every time you use that line of code. I'd like to catch and hide that exception inside cast() method.
Budric
Do you want asSubclass or getConstructor().newInstance().cast?
Tom Hawtin - tackline
+9  A: 

Because generic type information is erased at runtime, the standard way to cast to a generic type is with a Class object:

public static <T> T staticCast(Object arg, Class<T> clazz) {
    if (arg == null) return null;
    if (!clazz.isInstance(arg))
        return null;
    T result = clazz.cast(arg);
    return result;
}

Then call it like this:

a = Main.staticCast("B", MyClass.class);
Michael Myers
using Class.isInstance would be more direct.
Tom Hawtin - tackline
hmm, clazz? is that like a klass?
isInstance will only work if the object is an instance of clazz, not if it's a subclass of clazz. isAssignableFrom will work on anything that's castable to clazz, even subclasses.
Paul Tomblin
@Paul: The Javadocs say, "This method is the dynamic equivalent of the Java language instanceof operator. The method returns true if the specified Object argument is non-null and can be cast to the reference type represented by this Class object without raising a ClassCastException."
Michael Myers
+1  A: 

I agree with Ben, however, I prefer the following notation:

MyClass.class.cast(obj);
Maurice Perry