Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Java > nested generic HashMap problem

Reply
Thread Tools

nested generic HashMap problem

 
 
Chris Riesbeck
Guest
Posts: n/a
 
      04-26-2010
I've looked at the Java generics tutorial, Langer's FAQ, and similar
online pages, but I'm still don't see what, if anything, I can do to
make the last line (marked with the comment) compile, other than do a
typecast and suppress the unchecked warning. Am I asking the impossible
or missing the obvious?

import java.util.HashMap;

public class Demo<T> {
private Class<T> base;
private Cache cache;

Demo(Class<T> b, Cache c) { base = b; cache = c; }

Class<T> getBaseClass() { return base; }
T get(long id) { return cache.get(this, id); }
}

class Cache {
private HashMap<Class<?>, HashMap<Long, ?>> maps
= new HashMap<Class<?>, HashMap<Long, ?>>();

public <T> T get(Demo<T> demo, long id) {
return getMap(demo).get(id);
}

public <T> void add(Demo<T> demo) {
maps.put(demo.getBaseClass(), new HashMap<Long, T>());
}

private <T> HashMap<Long, T> getMap(Demo<T> demo) {
return maps.get(demo.getBaseClass()); // incompatible types
}
}
 
Reply With Quote
 
 
 
 
Lew
Guest
Posts: n/a
 
      04-26-2010
Chris Riesbeck wrote:
> I've looked at the Java generics tutorial, Langer's FAQ, and similar
> online pages, but I'm still don't see what, if anything, I can do to
> make the last line (marked with the comment) compile, other than do a
> typecast and suppress the unchecked warning. Am I asking the impossible
> or missing the obvious?
>
> import java.util.HashMap;
>
> public class Demo<T> {
> private Class<T> base;
> private Cache cache;
>
> Demo(Class<T> b, Cache c) { base = b; cache = c; }
>
> Class<T> getBaseClass() { return base; }
> T get(long id) { return cache.get(this, id); }
> }
>
> class Cache {
> private HashMap<Class<?>, HashMap<Long, ?>> maps
> = new HashMap<Class<?>, HashMap<Long, ?>>();
>
> public <T> T get(Demo<T> demo, long id) {
> return getMap(demo).get(id);
> }
>
> public <T> void add(Demo<T> demo) {
> maps.put(demo.getBaseClass(), new HashMap<Long, T>());
> }
>
> private <T> HashMap<Long, T> getMap(Demo<T> demo) {
> return maps.get(demo.getBaseClass()); // incompatible types
> }
> }


No way while you have the wildcards there. Suppressing unchecked warnings
will only expose you to ClassCastException.

Did you also read the free chapter on generics from Josh Bloch's /Effective
Java/ available at java.sun.com?

You only suppress unchecked warnings when you have documentable proof that you
cannot get a ClassCastException, and that documentation needs to be in the
program comments.

As defined, your 'Cache#maps' variable cannot even guarantee that the base
type of the 'Class' key matches the type of the value map's value.

Furthermore, you define all that in terms of concrete classes instead of
interfaces. Oops.

You might have better luck putting an upper bound (e.g., 'Foo') on the type of
'T' and have

public class Cache <Foo>
{
private Map <Class <? extends Foo>, Map <Long, Foo>> maps;
}

or perhaps

public class Cache <Foo>
{
private Map <Class <? extends Foo>, Map <Long, ? extends Foo>> maps;
}

but as long as you're holding disparate types in your so-called "cache" I
don't think you can avoid the risk of ClassCastException.

I could be wrong. Type analysis is tricky.

Whenever I find tricky generics questions like these, I find it pays to really
think very hard about what to assert about the types. Once I figure that out
the generics are a simple reflection of that analysis.

--
Lew
 
Reply With Quote
 
 
 
 
Chris Riesbeck
Guest
Posts: n/a
 
      04-26-2010
Lew wrote:
> Chris Riesbeck wrote:
>> I've looked at the Java generics tutorial, Langer's FAQ, and similar
>> online pages, but I'm still don't see what, if anything, I can do to
>> make the last line (marked with the comment) compile, other than do a
>> typecast and suppress the unchecked warning. Am I asking the
>> impossible or missing the obvious?
>> ...
>> public class Demo<T> {
>> ...
>> Class<T> getBaseClass() { return base; }
>> T get(long id) { return cache.get(this, id); }
>> }
>>
>> class Cache {
>> private HashMap<Class<?>, HashMap<Long, ?>> maps
>> = new HashMap<Class<?>, HashMap<Long, ?>>();
>>...
>> private <T> HashMap<Long, T> getMap(Demo<T> demo) {
>> return maps.get(demo.getBaseClass()); // incompatible types
>> }
>> }

>
> No way while you have the wildcards there. Suppressing unchecked
> warnings will only expose you to ClassCastException.
>
> Did you also read the free chapter on generics from Josh Bloch's
> /Effective Java/ available at java.sun.com?


Part of the above was meant to follow as best I could his pattern for
type-safe heterogeneous containers.

> As defined, your 'Cache#maps' variable cannot even guarantee that the
> base type of the 'Class' key matches the type of the value map's value.
>
> Furthermore, you define all that in terms of concrete classes instead of
> interfaces. Oops.


I agree. This was the shortest compilable example I could come up with
that still had the interactions I needed to support.

> You might have better luck putting an upper bound (e.g., 'Foo') on the
> type of 'T'
>
> [...snip...]
>
> Whenever I find tricky generics questions like these, I find it pays to
> really think very hard about what to assert about the types. Once I
> figure that out the generics are a simple reflection of that analysis.


T can be anything. What I can assert is that if the key is Demo<T> then
the nested map value is Map<T, Long>, where T = the return type of
Demo<T> getBaseClass(). I can't figure out if there's a way to write
that relationship in the type declaration.

I really appreciate the quick response, Lew. Thanks
 
Reply With Quote
 
markspace
Guest
Posts: n/a
 
      04-26-2010
Chris Riesbeck wrote:
> I've looked at the Java generics tutorial, Langer's FAQ, and similar
> online pages, but I'm still don't see what, if anything, I can do to
> make the last line (marked with the comment) compile, other than do a
> typecast and suppress the unchecked warning. Am I asking the impossible
> or missing the obvious?



Possibly. I've run into this before, that you basically can't use ? at
all for retrieving values of a type other than object, so you're going
to get a warning there no matter what you do.

If I follow your logic correctly, I think you can add a type parameter
to Cache, and that will allow you to use T instead of ? as a type
parameter for the hash map. Take a gander at the following and see if
it matches what you want to do:

package test;

import java.util.HashMap;

public class Demo<T> {
private Class<T> base;
private Cache<T> cache;

Demo(Class<T> b, Cache c) { base = b; cache = c; }

Class<T> getBaseClass() { return base; }
T get(long id) { return cache.get(this, id); }
}

class Cache<T> {
private HashMap<Class<T>, HashMap<Long, T>> maps
= new HashMap<Class<T>, HashMap<Long, T>>();

public T get(Demo<T> demo, long id) {
return getMap(demo).get(id);
}

public void add(Demo<T> demo) {
maps.put(demo.getBaseClass(), new HashMap<Long, T>());
}

private HashMap<Long, T> getMap(Demo<T> demo) {
return maps.get(demo.getBaseClass());
}
}
 
Reply With Quote
 
Daniel Pitts
Guest
Posts: n/a
 
      04-26-2010
On 4/26/2010 2:54 PM, Chris Riesbeck wrote:
> I've looked at the Java generics tutorial, Langer's FAQ, and similar
> online pages, but I'm still don't see what, if anything, I can do to
> make the last line (marked with the comment) compile, other than do a
> typecast and suppress the unchecked warning. Am I asking the impossible
> or missing the obvious?
>
> import java.util.HashMap;
>
> public class Demo<T> {
> private Class<T> base;
> private Cache cache;
>
> Demo(Class<T> b, Cache c) { base = b; cache = c; }
>
> Class<T> getBaseClass() { return base; }
> T get(long id) { return cache.get(this, id); }
> }
>
> class Cache {
> private HashMap<Class<?>, HashMap<Long, ?>> maps
> = new HashMap<Class<?>, HashMap<Long, ?>>();
>
> public <T> T get(Demo<T> demo, long id) {
> return getMap(demo).get(id);
> }
>
> public <T> void add(Demo<T> demo) {
> maps.put(demo.getBaseClass(), new HashMap<Long, T>());
> }
>
> private <T> HashMap<Long, T> getMap(Demo<T> demo) {
> return maps.get(demo.getBaseClass()); // incompatible types
> }
> }


Perhaps you should move the Map<Long, T> from Cache directly into Demo?

public class Demo<T> {
private Class<T> base;
private Map<Long, T> cache;

Demo(Class<T> b, Map<Long, T> c) { base = b; cache = c; }

Class<T> getBaseClass() { return base; }
T get(long id) { return cache.get(id); }
}

As a side note, I would make base and cache final.
--
Daniel Pitts' Tech Blog: <http://virtualinfinity.net/wordpress/>
 
Reply With Quote
 
Chris Riesbeck
Guest
Posts: n/a
 
      04-27-2010
Daniel Pitts wrote:
> On 4/26/2010 2:54 PM, Chris Riesbeck wrote:
>> I've looked at the Java generics tutorial, Langer's FAQ, and similar
>> online pages, but I'm still don't see what, if anything, I can do to
>> make the last line (marked with the comment) compile, other than do a
>> typecast and suppress the unchecked warning. Am I asking the impossible
>> or missing the obvious?
>>
>> import java.util.HashMap;
>>
>> public class Demo<T> {
>> private Class<T> base;
>> private Cache cache;
>>
>> Demo(Class<T> b, Cache c) { base = b; cache = c; }
>>
>>
>> class Cache {
>> private HashMap<Class<?>, HashMap<Long, ?>> maps
>> = new HashMap<Class<?>, HashMap<Long, ?>>();
>>
>>...
>>
>> private <T> HashMap<Long, T> getMap(Demo<T> demo) {
>> return maps.get(demo.getBaseClass()); // incompatible types
>> }
>> }

>
> Perhaps you should move the Map<Long, T> from Cache directly into Demo?


Which is actually where it started long ago, in the full set of classes,
before Factory came along. And probably the best way to go after all.
>
> public class Demo<T> {
> private Class<T> base;
> private Map<Long, T> cache;
>
> Demo(Class<T> b, Map<Long, T> c) { base = b; cache = c; }
>
> Class<T> getBaseClass() { return base; }
> T get(long id) { return cache.get(id); }
> }
>
> As a side note, I would make base and cache final.


I agree. It's a habit I still haven't gotten into, even though I const
like crazy in C++.

Thanks
 
Reply With Quote
 
Chris Riesbeck
Guest
Posts: n/a
 
      04-27-2010
markspace wrote:
> Chris Riesbeck wrote:
>> I've looked at the Java generics tutorial, Langer's FAQ, and similar
>> online pages, but I'm still don't see what, if anything, I can do to
>> make the last line (marked with the comment) compile, other than do a
>> typecast and suppress the unchecked warning. Am I asking the
>> impossible or missing the obvious?

>
>
> Possibly. I've run into this before, that you basically can't use ? at
> all for retrieving values of a type other than object, so you're going
> to get a warning there no matter what you do.
>
> If I follow your logic correctly, I think you can add a type parameter
> to Cache, and that will allow you to use T instead of ? as a type
> parameter for the hash map. Take a gander at the following and see if
> it matches what you want to do:
>
> package test;
>
> import java.util.HashMap;
>
> public class Demo<T> {
> private Class<T> base;
> private Cache<T> cache;


Yes, if the Cache is in Demo, then I can do it. That was the original
design. Time to chalk this up as a blind alley.

Thanks, everyone, for the comments and problem solving.
 
Reply With Quote
 
markspace
Guest
Posts: n/a
 
      04-27-2010
Chris Riesbeck wrote:
>
> Yes, if the Cache is in Demo, then I can do it.



Just a note, in case you missed it: I didn't put the Cache inside of
Demo, all I did was add a type parameter to Cache.


> That was the original
> design. Time to chalk this up as a blind alley.



I think, given what you've shown us, you either have to remove the T
type information from the return types in Cache (if you don't have a
type, how can you guarantee that two different method calls actually
correlate in their type? That's why the ? is messing you up), or you
have to parameterize Cache, as I did, so the compiler can check your work.

Also, I didn't modify any other code. Your program appears to me to be
correct, so you could add a SupperssWarnings to the return type. But it
might be better not to: There's always maintenance to consider, and
keeping that SupperssWarnings valid through many code revisions,
possibly not done by you, might not be easy or even possible.
 
Reply With Quote
 
Lew
Guest
Posts: n/a
 
      04-27-2010
Daniel Pitts wrote:
>> As a side note, I would make base and cache final.


Chris Riesbeck wrote:
> I agree. It's a habit I still haven't gotten into, even though I const
> like crazy in C++.


The semantics of Java's 'final' differ from C++'s 'const' somewhat, as with so
many things that are similar between the languages but not quite the same.

--
Lew
 
Reply With Quote
 
Chris Riesbeck
Guest
Posts: n/a
 
      04-27-2010
markspace wrote:
> Chris Riesbeck wrote:
>>
>> Yes, if the Cache is in Demo, then I can do it.

>
>
> Just a note, in case you missed it: I didn't put the Cache inside of
> Demo, all I did was add a type parameter to Cache.


I did mis-read that.

> I think, given what you've shown us, you either have to remove the T
> type information from the return types in Cache (if you don't have a
> type, how can you guarantee that two different method calls actually
> correlate in their type?


I don't follow the "don't have a type" part here. The correlation I was
trying to capture was

T get(Demo<T>, long)

using an underlying Map(Demo<T>, Map<long, T>). That seems to me to be
well-defined, just not definable in Java.

> Also, I didn't modify any other code. Your program appears to me to be
> correct, so you could add a SupperssWarnings to the return type. But it
> might be better not to: There's always maintenance to consider, and
> keeping that SupperssWarnings valid through many code revisions,
> possibly not done by you, might not be easy or even possible.


Yes. I try to keep all SuppressWarnings limited to those cases forced by
old libraries or corner cases in Java that have no alternative. In this
case, in the larger design, there's another approach that doesn't need
to suppress unchecked warnings, so I'll stick with that.

Thanks
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
reuse HashMap$Entry (or HashMap in total) to avoid millions of allocations Vince Darley Java 4 03-02-2010 07:48 AM
generic interfaces with generic methods Murat Tasan Java 1 02-03-2009 12:17 PM
java.util.Properties extending from HashMap<Object, Object> insteadof HashMap<String, String> Rakesh Java 10 04-08-2008 04:22 AM
Generic class in a non generic class nramnath@gmail.com Java 2 07-04-2006 07:24 AM
Generic Newbie HashMap Query MrFredBloggs@hotmail.com Java 9 05-02-2005 04:01 PM



Advertisments