Budric
Budric

Reputation: 3709

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.

Upvotes: 6

Views: 567

Answers (5)

Michael Myers
Michael Myers

Reputation: 192055

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);

Upvotes: 10

TofuBeer
TofuBeer

Reputation: 61534

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);
    }
}

Upvotes: 6

Maurice Perry
Maurice Perry

Reputation: 32831

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

MyClass.class.cast(obj);

Upvotes: 1

Paul Tomblin
Paul Tomblin

Reputation: 182878

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;
}

Upvotes: 1

Ben S
Ben S

Reputation: 69412

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.

Upvotes: 4

Related Questions