views:

944

answers:

5

I'm trying to catch a ClassCastException when deserializing an object from xml.

So,

    try {
        restoredItem = (T) decoder.readObject();
    } catch (ClassCastException e){
        //don't need to crash at this point,
       //just let the user know that a wrong file has been passed.
    }

And yet this won't as the exception doesn't get caught. What would you suggest?

A: 

I would suggest you not use exception logic at all. Exceptions are for exceptional conditions; I'm not sure the user giving you an incorrect file qualifies.

I think you'd be wiser to use the instanceof operator to check whether the deserialized object is the right type.

Edit:

Wait, this is no help at all! Totally missed the parametrized type; where I work we're stuck in 1.4-land.

Will Wagner
A: 

Well, I can't use instanceof operator as the method is a parametrized one:

public T restore(String from){
...
restoredItem = (T) decoder.readObject();
...
}

And generics in Java are compile-time only.

alex
Wouldn't that suggest that the cast to T is actually done by the caller, not by the method itself?
Chris Jester-Young
Not sure I quite get your point. As a quick hack I return an Object from restore(..) and do an instanceof check at a higher level, no generics involved (as they don't help anyways). Still, this is ugly.
alex
+3  A: 

The code in the question should give you an unchecked cast warning. Listen to -Xlint.

All the compiler knows about T is its bounds, which it probably doesn't have (other than explicitly extending Object and a super of the null type). So effectively the cast at runtime is (Object) - not very useful.

What you can do is pass in an instance of the Class of the parameterised type (assuming it isn't generic).

class MyReader<T> {
    private final Class<T> clazz;
    MyReader(Class<T> clazz) {
        if (clazz == null) {
            throw new NullPointerException();
        }
        this.clazz = clazz;
    }
    public T restore(String from) {
        ...
        try {
            restoredItem = clazz.cast(decoder.readObject());
            ...
            return restoredItem;
        } catch (ClassCastException exc) {
            ...
        }
    }
}

Or as a generic method:

    public <T> T restore(Class<T> clazz, String from) {
        ...
        try {
            restoredItem = clazz.cast(decoder.readObject());
            ...
Tom Hawtin - tackline
+2  A: 

There will not be any ClassCastException, except when your T has some base:

public class GenericsTest
{
    public static void main(String[] args)
    {
        System.out.println(cast(Integer.valueOf(0)));
        System.out.println(GenericsTest.<Long> cast(Integer.valueOf(0)));
        System.out.println(GenericsTest.<Long> cast("Hallo"));

        System.out.println(castBaseNumber(Integer.valueOf(0)));
        System.out.println(GenericsTest.<Long> castBaseNumber(Integer.valueOf(0)));
        System.out.println(GenericsTest.<Long> castBaseNumber("Hallo"));
    }

    private static <T extends Number> T castBaseNumber(Object o)
    {
        T t = (T)o;
        return t;
    }

    private static <T> T cast(Object o)
    {
        T t = (T)o;
        return t;
    }
}

In the above example, there will be no ClassCastException in the first 5 calls to cast and castBaseNumber. Only the 6th call throws a ClassCastException, because the compiler effectively translates the cast() to return (Object) o and the castBaseNumber() to return (Number)o;. Wenn you write

String s = GenericsTest.<Long> cast("Hallo");

You would get a ClassCastException, but not whithin the cast-method, but at the assignment to s.

Therefore I do think, your "T" is not just "T", but "T extends Something". So you could check:

Object o = decoder.readObject();
if (o instanceof Something)
    restoredItem = (T) o;
else 
    // Error handling

But this will still lead to an error later, when the you use your class.

public Reader<T extends Number>{...}

Long l = new Reader<Long>("file.xml").getValue(); // there might be the ClassCastException

For this case only Tom's advise might help.

Tobias Schulte
A: 

If you can't use instaceof you might be able to use the isAssignableFrom method on Class

John Nilsson