views:

166

answers:

3

// legacy code

void setCacheValue(String name, Object value){
    getServletContext().setAttribute(name, value);
}
Object getCacheValue(String name){
    return getServletContext().getAttribute(name);
}

// so I want to use generic for "type safety"

// first, set method seems working perfectly
<T> void setCacheObject(String name, T value){
    getServletContext().setAttribute(name, value);
}

// then, here comes the trouble

<T> T getCacheValue(String name){    
    // of course, I cannot change servlet class - it returns Object type
    Object value = getServletContext().getAttribute(name);
    // would this work:     
    return  (T) value;
    // this cast is meaningless... but how should I do it?
}

// This is what I what to achieve in my clean calling code:

{
    double x = 1.2;
    setCacheValue("x", x);
    //...
    // later
    Double value = getCacheValue("x");
    // too bad cannot use primitive type - it cannot handle null

}

So, what is the correct way of doing this?

+2  A: 

That's indeed not possible. You'll need to pass the "concrete" T somehow as method argument so that the actual type is known during runtime. Commonly used approach is passing it as Class<T>, so that you can make use of Class#cast():

<T> T getCacheValue(String name, Class<T> type) {
    return type.cast(getServletContext().getAttribute(name));
}

You can use it as follows:

Double value = getCacheValue("x", Double.class);
BalusC
+1 common problem - standard approach
leonbloy
great. i found class-casting does two thing: it suppress the compile warning, and it checks the type at runtime -- although it may be too late but it's better than not check at all.(with a price of having to pass an extra parameter)
joejax
A: 

Map generics support a type on all values of the map, not a different type for specific values. You can see how to fake it here. Basically the idea is you have to have the type safety on the key, where the key has a generic type on it which exists simply to associate with the value.

At the end of the day you won't be able to do it without an unsafe cast, but you can do it in a way that makes it extremely unlikely that there is a problem with a cast and in a way which is typesafe to the users of your class.

Yishai
Interesting idea. Only note that the attribute map of the servlet context only accepts String keys. You would need to put another Map in it and use it accordingly. –
BalusC
A: 

Actually that compiles, too:

public class Test
{
    <T> T getCacheValue(String name){    
        // of course, I cannot change servlet class - it returns Object type
        Object value = getServletContext().getAttribute(name);
        // would this work:     
        return  (T) value;
        // this cast is meaningless... but how should I do it?
    }

    public static void main(String... args)
    {
        Test t = new Test();
        Double double = t.<Double>getCacheValue("Double");
    }
}

It is kind of pointless (maybe if you add a typecheck) but I found it interesting to know.

Daff