Alex Rivera | Logout

Java generics and typecasting

Asked 2013-04-20T09:09:13.010
19

I have a badly created container object that holds together values of different java types(String, Boolean etc ..)

public class BadlyCreatedClass {
    public Object get(String property) {
        ...;
    }
};

And we extract values from it in this way

String myStr = (String) badlyCreatedObj.get("abc");
Date myDate = (Date) badlyCreatedObj.get("def");

I am forced to write some new code using this object and I am trying to see if there is clean way to do this. More specifically which method from the below is preferred ?

Explicit Cast

String myStr = (String) badlyCreatedObj.get("abc")
Date myDate = (Date) badlyCreatedObj.get("def");

Using generic cast

public <X> X genericGet(String property) {

}

public String getString(String property) { 
return genericGet(property); 
}

public Date getDate(String property) { 
return genericGet(property); 
}

Using Class.cast

<T> T get(String property, Class<T> cls) {
    ;
}

I have gone through several related questions on SO Java generic function: how to return Generic type , Java generic return type all of them seem to say the such typecasting is dangerous, ALthough I dont see much difference between the three, given this which method would you prefer ?

Thanks

Edit
Report

1 Answer

3

The generic cast approach causes the compiler to emit an unchecked warning. An unchecked warning indicates that the cast is question is not (fully) checked at runtime, i.e. it may succeed, even if the value is not of the proper type. This can cause a variable to hold a value not compatible with its declared type, a situation the Java Language Specification calls heap pollution.

The following program demonstrates this:

class Holder {
    Object value;

    Holder(Object value) {
        this.value = value;
    }

    <T> T get() {
        return (T) value;
    }
}

class C<T> {
    T value;

    C(Holder h) {
        value = h.get();
    }
}

public class Test {
    public static void main(String [] args) throws IOException {
        Holder holder = new Holder("Hello");
        C<Integer> c = new C<Integer>(holder);
        System.out.println("I just put a String into a variable of type Integer");

        // much later, possibly in a different part of your program
        c.value.longValue(); // throws ClassCastException
    }
}

Therefore, I strongly recommend using a checked cast. Both the ordinary cast (your first approach), and the reflective cast (your third approach) are checked. However, the reflective cast will not work with parametrized types (List<String>.class doesn't compile ...).

The simplest and most flexible safe solution therefore is the ordinary cast.

answered 2013-04-20T11:16:40.997

Your Answer