I am looking for a way to create a HashMap that maps to mixed types.
For example, the following would work in Python (note that a Python dict
is similar to a Java HashMap
):
d = dict()
d["str"] = "This is a string"
d["flt"] = 1.23
d["int"] = 5
and when accessing d[x]
, the appropriate type would be returned (string, float, int in this example).
In Java, I could go with a HashMap<String, Object>
, but I don't really have a way to immediately tell what type each Object is.
So instead, I'm currently trying to do is create a MixedType
class that holds an object, but also its original type information, so that I can downcast it later. For example:
public class MixedMap
public static class MixedType
{
public Class<?> cls;
public Object val;
public MixedType(Class<?> c, Object v)
{
this.cls = c;
this.val = v;
}
}
public static void main(String args[])
{
MixedType m1 = new MixedType(String.class, "This is a String");
System.out.println((m1.cls)m1.val);
}
}
Note that this is what I'm trying to do :-)
as it's currently not compiling complaining that m1 cannot be resolved to a type
.
So my question is, how can I fix this? Other ways to achieve the same goal would be welcomed, but please don't answer telling me that this limits the type checking ability of the compiler -- I know that and am OK with it.
Thanks in advance.
The proper thing to do is m1.cls.cast(m1.val)
, but you almost certainly won't gain anything as compared to a plain Map<String, Object>
. A simpler approach that would get you exactly as much type safety and exactly the same information -- which is to say, not much -- would just be to use the getClass()
method on the Object
values in your map.
None of this will accomplish your goal of being able to say String value = map.get("str")
-- nothing short of an outright cast would do that. You could do
Object value = map.get("str");
if (value instanceof String) {
// do stringy things
}
or something along those lines.
If you know the types of the values at their use points at compile time, then by far the best solution is just to go ahead and do the casts at the usage points. The "ideal" way to do configuration like this in general is not to use a Map
, but to build a customized Configuration
class that knows its fields and their types at compile time, but if you want to use a Map
, then just doing the casts directly is probably your best bet.
I've implemented something similar to this before. You can use generics to assist. What you want is a MixedKey
, that you can use as a key into a MixedMap
. Something along these lines:
public class MixedKey<T> {
public final Class<T> cls;
public final String key;
public MixedKey(T cls, String key) {
this.key = key;
this.cls = cls;
}
// also implement equals and hashcode
}
.
public class MixedMap extends HashMap<MixedKey<?>,Object> {
public <T> T putMixed(MixedKey<T> key, T value) {
return key.cls.cast(put(key, value));
}
public <T> T getMixed(MixedKey<T> key) {
return key.cls.cast(get(key));
}
}
The nice thing about doing something like this is it will give you a compile-time error if you use the wrong type by mistake, something you don't get with direct casting:
MixedKey<Integer> keyForIntProperty
= new MixedKey<Integer>(Integer.class, "keyForIntProperty");
// ...
String str = mixedMap.getMixed(keyForIntProperty); // compile-time error
vs.
String str = (String)map.get("keyForIntProperty"); // run-time error only
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With