Java Map with Values Limited by Key's Type Parameter

Java map with values limited by key's type parameter

You're not trying to implement Joshua Bloch's typesafe hetereogeneous container pattern are you? Basically:

public class Favorites {
private Map<Class<?>, Object> favorites =
new HashMap<Class<?>, Object>();

public <T> void setFavorite(Class<T> klass, T thing) {
favorites.put(klass, thing);
}

public <T> T getFavorite(Class<T> klass) {
return klass.cast(favorites.get(klass));
}

public static void main(String[] args) {
Favorites f = new Favorites();
f.setFavorite(String.class, "Java");
f.setFavorite(Integer.class, 0xcafebabe);
String s = f.getFavorite(String.class);
int i = f.getFavorite(Integer.class);
}
}

From Effective Java (2nd edition) and this presentation.

Java generics enforce same type for keys and values of map

You can do this, but you have to roll your own wrapper on top of a Map:

class MyTypeSafeMap {
private Map<Key<?>, Value<?>> map;
public <T> void put(Key<T> key, Value<T> value) {
map.put(key, value);
}

public <T> Value<T> get(Key<T> key) {
return (Value) map.get(key);
// we know it's safe, but the compiler can't prove it
}
}

Compare e.g. Guava's ClassToInstanceMap.

limit map key and value types - more complicated

Thank you all for your answers, it really helped me come to the following solution.

The answer from flicken showed me the way: I have to extract some code into a parameterized method. but instead of extracting validators.get() in a method, I can extract the whole validation process. Doing so, I can use programmatic cast (which I assume OK since I control the coherence of key to values map):

public void validate(Object o) {
Field[] fields = getFields(o.getClass());
for (Field field : fields) {
Annotation[] annotations = field.getAnnotations();
for (Annotation annotation : annotations) {
AnnotatedFieldValidator<? extends Annotation> validator =
validators.get(annotation.annotationType());
if (validator != null) {
doValidate(field, validator, annotation, o);
}
}
}
}

And then, the doValidate() method is as follows:

private <A extends Annotation> void doValidate(Field field, 
AnnotatedFieldValidator<A> validator, Annotation a, Object o) {
// I assume this is correct following only access to validators Map
// through addValidator()
A annotation = validator.getSupportedAnnotationClass().cast(a);
try {
validator.validate(field, annotation, bean, beanName);
} catch (IllegalAccessException e) {
}
}

No cast (OK, except Class.cast()...), no unchecked warnings, no raw type, I am happy.

Generic Map of Generic key/values with related types

Your implementation is correct. There's no "better" way of doing it (if there is such a thing is "better" in code, which is another issue..)

Minor fixes:

  • <V extends Object> is equivalent to V which is less verbose
  • Class<? extends Object> is equivalent to Class<?> which is less verbose
  • You can use the @SuppressWarnings("unchecked") annotation to tell your compiler that the cast is safe

Java Generics - Map with Lists of Limited Size

The issue that you've got is that value.subList returns a List<Object>, not a V. There is no requirement for the subList method to return a List of the same type as itself: indeed, you often can't return a list of the same type, because a view of a list is fundamentally not the same as the list itself.

You should declare it as:

public class LimitedSizeMap<K, V> extends HashMap<K, List<V>> {

If you really want to have the List in the V, I suppose you can do it by supplying an extra argument to the constructor, which does the "limiting" for you:

public class LimitedSizeMap<K, V extends List<Object>> extends HashMap<K, V> {
private final Function<? super V, ? extends V> trimmer;

public LimitedSizeMap(Function<? super V, ? extends V> trimmer) { this.trimmer = trimmer; }

@Override
public V put(K key, V value) {
return super.put(key, trimmer.apply(value));
}
}

(or you can make trimmer an abstract method which you have to implement to instantiate the class).

You then just have to work out how you can implement that function. It's easy if your V is List<Object>; it's less easy otherwise.

I'd say the awkwardness of this far outweights whatever benefit you think you'd have to have the List-ness in the type arguments. Honestly, the bigger issue I see with this class is its name implies that the Map has limited size, not the lists stored in the values.


I'd also say that extending HashMap is a rather dubious thing to do.

I'd recommend extending AbstractMap<K, List<V>> and implementing the methods to delegate to a wrapped HashMap:

public class LimitedSizeMap<K, V> extends AbstractMap<K, List<V>> {
private final HashMap<K, List<V>> delegate = new HashMap<>();

@Override
public V put(K key, V value) {
if (this.limit > 0) {
return delegate.put(key, value.subList(0, Math.min(value.size(), this.limit)));
} else {
return delegate.put(key, value);
}
}

// ... implement the other methods as required: see Javadoc.
}

How to add values for java generic map with undetermined ? value type

Question (1)

? is called wildcard generic type.It can be used with any type,
but you need to specify type fist as Map is an abstract class.

Question (2)

for you to give value to this map its either you use Upper bound or Lower bound.

The following are guidelines when using upper bound or lower bound

? extends Type   - is used for read access only
? super Type - is used for write access only.

PECS in short Produces(write acccess) -uses Extends while Consumes(read access) - uses Super

Map<String, ? super Object> map = new HashMap<>(3);//OK
map.put("abc",Optional.of(5));
map.put("kk","xyz");
map.forEach((k,v)->System.out.println(k+"\t"+v));

Output

kk  xyz
abc Optional[5]

Process finished with exit code 0

Additional Notes

Unbounded wildcard ?

 List<?> l = new ArrayList<String>();

Wildcard with an upper bound ? extends type

 List<? extends Exception> l = new ArrayList<RuntimeException>();

Wildcard with a lower bound ? super type

 List<? super Exception> l = new ArrayList<Object>();

Java map with values limited by key's type parameter

You're not trying to implement Joshua Bloch's typesafe hetereogeneous container pattern are you? Basically:

public class Favorites {
private Map<Class<?>, Object> favorites =
new HashMap<Class<?>, Object>();

public <T> void setFavorite(Class<T> klass, T thing) {
favorites.put(klass, thing);
}

public <T> T getFavorite(Class<T> klass) {
return klass.cast(favorites.get(klass));
}

public static void main(String[] args) {
Favorites f = new Favorites();
f.setFavorite(String.class, "Java");
f.setFavorite(Integer.class, 0xcafebabe);
String s = f.getFavorite(String.class);
int i = f.getFavorite(Integer.class);
}
}

From Effective Java (2nd edition) and this presentation.



Related Topics



Leave a reply



Submit