Question

I was wondering what is the data type of the template variable if the return is set to a template. I have seen this in a code somewhere but I do not know where does it cast the value retrieved from the session.

public class RequestObject {
    public <T> T getFromSessionMap(String sessionKey) {
        return (T)session.getAttribute(sessionKey);
    }
}

The code for this outside is:

MyClassType type = request.getFromSessionMap("abc");

The line encounters ClassCastException when casting to my object. But when I add to watch session.getAttribute("abc"), it shows that the type is MyClassType. Any help would be appreciated.

Apparently this special code for using template makes the return of getFromSessionMap a variable type and hence no need for a cast. This works on all cases but suddenly it failed in one part of the code.

Was it helpful?

Solution

Apparently this special code for using template makes the return of getFromSessionMap a variable type and hence no need for a cast.

Fundamentally, there has to be a typecast somewhere between getting the result of session.getAttribute(sessionKey) and the assignment to MyClassType. The Java language (and the JVM) will not allow some object that is not a MyClassType instance (or a subtype thereof) to be assigned to a MyClassType variable.

No matter how you write the code, a typecast has to occur. And since the attribute (apparently) is not a MyClassType (or subtype), you are getting a ClassCastException.

So the real question is why aren't you getting a compilation error? And the answer is the @SuppressWarnings("unchecked")! If you removed that warning, you would get an "unsafe type conversion" error message for this line:

return (T) session.getAttribute(sessionKey);

In truth, Java cannot do a (real) type cast to a generic type. And that is what the warning / error message is there to point out. In fact, once the code has been compiled, this code

public <T> T getFromSessionMap(String sessionKey) {
    return (T)session.getAttribute(sessionKey);
}

is actually no different in meaning from this:

public Object getFromSessionMap(String sessionKey) {
    return session.getAttribute(sessionKey);
}

Technically speaking this is called "type erasure".

So where is the type checking / typecast actually occurring? The answer is in this line:

MyClassType type = request.getFromSessionMap("abc");

Even though you haven't written a typecast here, the code generated by the Java compiler does a typecast before assigning the value to type. It has to. Because as far as it knows, the instance it is assigning could be any object type.

Other posters have suggested adding a Class argument to getFromSessionMap. By itself this does absolutely nothing. If you also replace the body of the method with:

return clazz.cast(session.getAttribute(sessionKey));

you will cause the method to actually do a real type check. But this only cause ClassCastException to be thrown at a different place. And the assignment statement will still do a hidden type cast!!

OTHER TIPS

In the question's example, the return type is T. The erased type will be Object as, implicitly, T extends Object. The actual cast is performed in the bytecode of the calling method (you can use javap -c to see that).

Generally, you should keep the number of "top-level" objects in sessions as small as possible. One of the benefits of doing that, is there is no longer a need for hacky methods like these.

Any help would be appreciated because my code encounters ClassCastException.

If you're getting a ClassCastException, that means you're trying to cast something into something it isn't, like in the following code:

Map session = new HashMap();
session.put("date", "2009-11-12");
Date today = (Date) session.get("date"); // tries to convert String to Date

The ClassCastException should have an enlightening detail message, like "java.lang.String cannot be cast to java.util.Date".

You use type cast within the method body ('(T)session.getAttribute(sessionKey)').

That means that you explicitly say to compiler 'I'm absolutely sure that returned object IS-A T and ready to handle error if it is not'.

Here your assumption about attribute type was incorrect and you got an error. So, everything is correct and runtime already provides you with the real attribute object type that is not IS-A MyClassType.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top