Uncheck Warning

W

Wojtek

In the context of a servlet, if I have:
---------------------------
HashMap<String,String> foo = new HashMap<String,String>();
session.setAttribute("foo",foo);
---------------------------

Ok, this is legal and it works. Now to get it out...

---------------------------
HashMap<String,String> bar =
(HashMap<String,String>) session.getAttribute("foo");
---------------------------

This causes an "unchecked" warning and I need to use a @suppress
annotation for the entire method.

So how do I cast from Object to HashMap<String,String> ?
 
A

Arne Vajhøj

Wojtek said:
In the context of a servlet, if I have:
---------------------------
HashMap<String,String> foo = new HashMap<String,String>();
session.setAttribute("foo",foo);
---------------------------

Ok, this is legal and it works. Now to get it out...

---------------------------
HashMap<String,String> bar =
(HashMap<String,String>) session.getAttribute("foo");
---------------------------

This causes an "unchecked" warning and I need to use a @suppress
annotation for the entire method.

So how do I cast from Object to HashMap<String,String> ?

My guess is that you can't because session can not guarantee
that getAttribute will return a HashMap<String,String>.

Arne
 
L

Lew

Arne said:
My guess is that you can't because session can not guarantee
that getAttribute will return a HashMap<String,String>.

You can. The free chapter of /Effective Java/ about generics explains one
idiom: assign the Map to a temporary variable so that you can narrow the
@SuppressWarnings("unchecked") down to the minimum. (You don't have to guard
the whole method.)

The warning is because you can't do generic casts without a warning.

@SuppressWarnings( "unchecked" )
Map <String,String> bar = // I took the liberty of widening the target type
= (Map <String,String>) session.getAttribute("foo");

The annotation can precede a class, method or declaration. It's good to
minimize the effect.
 
W

Wojtek

Lew wrote :
You can. The free chapter of /Effective Java/ about generics explains one
idiom: assign the Map to a temporary variable so that you can narrow the
@SuppressWarnings("unchecked") down to the minimum. (You don't have to guard
the whole method.)

The warning is because you can't do generic casts without a warning.
@SuppressWarnings( "unchecked" )
Map <String,String> bar = // I took the liberty of widening the target
type
= (Map <String,String>) session.getAttribute("foo");

The annotation can precede a class, method or declaration. It's good to
minimize the effect.

Yes, I want to narrow the scope of the suppression.

Ideally I want to get rid of it alltogether.
 
A

Arne Vajhøj

Lew said:
You can. The free chapter of /Effective Java/ about generics explains
one idiom: assign the Map to a temporary variable so that you can narrow
the @SuppressWarnings("unchecked") down to the minimum. (You don't have
to guard the whole method.)

The warning is because you can't do generic casts without a warning.

@SuppressWarnings( "unchecked" )
Map <String,String> bar = // I took the liberty of widening the target
type
= (Map <String,String>) session.getAttribute("foo");

The annotation can precede a class, method or declaration. It's good to
minimize the effect.

OK. But it does not remove the problem - it just limit the scope
of the workaround.

Arne
 
W

Wojtek

Arne Vajhøj wrote :
My guess is that you can't because session can not guarantee
that getAttribute will return a HashMap<String,String>.

The session cannot guarantee it at all. However *I* know what I put
into it using that key. When I use the same key to get it out, then I
KNOW what it is.

It should be the same as putting in a Long, then getting it out with a
(Long) cast.

If the cast is wrong, then a ClassCastException is thrown. But I should
be able to cast it to what I put it in as.
 
A

Arne Vajhøj

Wojtek said:
Arne Vajhøj wrote :

The session cannot guarantee it at all. However *I* know what I put into
it using that key. When I use the same key to get it out, then I KNOW
what it is.

It should be the same as putting in a Long, then getting it out with a
(Long) cast.

If the cast is wrong, then a ClassCastException is thrown. But I should
be able to cast it to what I put it in as.

Yes. But the idea behind generics is that compilers are reliable and
programmers are not.

Arne
 
L

Lew

Arne said:
OK. But it does not remove the problem - it just limit the scope
of the workaround.

That is true. The problem is inherent with erasure. There will always be
places where you have to suppress warnings, but if you control them correctly
and can lock in the correct type as an invariant, as in the OP's example, then
you are relatively safe.

/Effective Java/ discusses the circumstances under which you can successfully
suppress warnings. Due to erasure, the genericity only goes so far.

Reifiable generics would be nice.
 
L

Lew

Yes. But the idea behind generics is that compilers are reliable and
programmers are not.

Yes, but there is a limit. Worst case, generics are no worse than before
generics. With raw types, there would be the same responsibility. With
generics, the risk is limited to the declaration where the cast happens.
After that point, i.e., for the rest of the usage of the resulting reference,
the compiler can do its job. For example, having successfully extracted a
'Map<String, String>', the program can be sure not to accidentally enter a
'Foo' for key or value into the 'Map'.

Joshua Bloch's chapter on this is freely available.
<http://java.sun.com/docs/books/effective/generics.pdf>
 
W

Wojtek

Arne Vajhøj wrote :
Yes. But the idea behind generics is that compilers are reliable and
programmers are not.

I can see that point (and I do like it BTW).

But getting an object out of a general container, then casting it to a
specific object should be allowed for generics also.

The compiler does not know that I put in a Long, yet it allows me to
cast to it when I retrieve it. Why not for generics?
 
W

Wojtek

Lew wrote :

Sigh. Ok, I can see this now. Generics is a compiler trick, and is not
passed on into the byte code.

Since the getAttribute occurs during runtime, all the generics
information is missing, and so cannot be verified. So I must provide
that assurance to the compiler via the supress annotation.

As it is, I have narrowed down the scope to a two line method which
simply extracts the HashMap, then returns it. So the suppress warning
is limited down to that small method.

I suddenly feel like I am petting the compiler and giving it treats so
it stops whining :)
 
A

Arne Vajhøj

Lew said:
Yes, but there is a limit. Worst case, generics are no worse than
before generics.

I agree. Generics has made most collections usage much more
type safe and that is a good thing.

They also added quite some complexity, but that is another discussion.

Arne
 
A

Arne Vajhøj

Wojtek said:
The compiler does not know that I put in a Long, yet it allows me to
cast to it when I retrieve it. Why not for generics?

Type erasure limits what can happen at runtime and that
had caused the compiler people to make some decisions.

Arne
 
A

Arne Vajhøj

Wojtek said:
I suddenly feel like I am petting the compiler and giving it treats so
it stops whining :)

I think I will use this method.

"javac - now be a good little compiler and compile
my program - then I will install you on a shiny new
harddrive"

:)

Arne
 
D

Daniel Pitts

Wojtek said:
Lew wrote :

Yes, I want to narrow the scope of the suppression.

Ideally I want to get rid of it alltogether.
Unfortunately you can't rid yourself of it.

The reason is that session.getAttribute() returns Object, so you *have*
to cast it. You can use "instanceof Map", but because of erasure, you
can't validate the <String,String> of the map, and you are *still* open
to bugs.

One approach would be to have
class MySessionAttributes {
Map<String, String> foo;
// other things
}

session.setAttribute("myAttributes", mySessionAttribute);


Object obj = session.getAttribute("myAttributes");
if (!(obj instanceof MySessionAttribute)) {
throw new Exception(
"Programmer error: myAttributes is not of the right type.");
}
MySessionAttribute foo = MySessionAttribute.class.cast(obj);
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,819
Latest member
masterdaster

Latest Threads

Top