Prev: Quartz with Spring: more jobs with different input argument
Next: call jni function dynamically without getting a JNIEnv handle as an argument.
From: Chris Riesbeck on 26 Apr 2010 17:54 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 } }
From: Lew on 26 Apr 2010 18:18 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
From: Chris Riesbeck on 26 Apr 2010 18:43 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
From: markspace on 26 Apr 2010 18:56 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()); } }
From: Daniel Pitts on 26 Apr 2010 19:47
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/> |