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 toV
which is less verboseClass<? extends Object>
is equivalent toClass<?>
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
Android Service Needs to Run Always (Never Pause or Stop)
Error: Unable to Run Mksdcard Sdk Tool
How to Get the Current Screen Orientation
Best Way to Compare Dates in Android
Android - Change App Theme on Onclick
Convert JSONarray to String Array
Running Shell Commands Though Java Code on Android
How to Paginate Queries by Combining Query Cursors Using Firestorerecycleradapter
Android Adding Simple Animations While Setvisibility(View.Gone)
Read Content from Files Which Are Inside Zip File
The Literal Xyz of Type Int Is Out of Range
Java Stack Overflow Error - How to Increase the Stack Size in Eclipse
How to Count the Number of Matches for a Regex