Java 以变量泛型作为值的映射
Java Map with variable generics as values
所以这是一个有点棘手的问题(对我来说)。
我有一个通用对象。称它为我的对象。这个对象有一个 return 类型为 T:
的方法
public class MyObject<T>
{
private T _t;
public MyObject(T t)
{
_t = t;
}
//...
public T get()
{
return _t;
}
}
(显然我的 "MyObject" 做得更多,但这就是要点)。
现在,我想要一张这种类型的地图:
Map<String, MyObject<?>> m = new HashMap<>();
我希望能够使用一些预定义的字符串名称来获取地图,并且这些地图可以是任何 MyObject。例如,我可以调用:
m.put("map_1", new MyObject<String>("String"));
m.put("map_2", new MyObject<Integer>(new Integer(3));
m.put("map_3", new MyObject<Long>(new Long(5));
等等
但是 - 这是棘手的部分 - 当我从地图中获取一些值时,我希望地图 "remember" MyObject 的参数化类型。使用
m.get("map_1");
会return一个
MyObject<Object>
类型,因为地图被定义为包含
MyObject<?>
值。因此:
m.get("map_1").get() // <-- This is an Object, not a String!
为了能够获得有关 MyObject 获取的对象的正确 - 完整 - 信息,可以进行哪些修改(如果有的话),以便调用最后一行 (m.get("map_1")) 会 return 一个
MyObject<String>
谢谢:)
阿米尔。
您可以获得 class.
Class c = m.get("map_1").get().getClass();
if (String.class.equals(c)) {
System.out.println("its a String");
}
这是一个完整的测试。
public class GenericsTest {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
Map<String, MyObject<?>> map = new HashMap<>();
MyObject<String> obj = new MyObject<>("hello");
map.put("greeting", obj);
Class c = map.get("greeting").get().getClass();
if (String.class.equals(c)) {
System.out.println("its a String");
}
}
static class MyObject<T> {
T t;
public MyObject(T t) {
this.t = t;
}
T get() {
return t;
}
}
}
来自 Joshua Bloch 的 Effective Java 的类型安全异构容器可能在这里工作。基本上你添加一个 Class
对象来表示类型。
public class MyObject<T>
{
private T _t;
private Class<T> type;
public MyObject( Class<T> type, T t)
{
_t = t;
this.type = type;
}
//...
public T get()
{
return _t;
}
public Class<T> getType() { return type; }
}
那么你可以这样做:
public <T> T get( Map<String, MyObject<?>> map, String key, Class<T> type ) {
return type.cast( m.get( key ).get() );
}
它是安全的并且可以编译,但是如果你输入错误的类型会抛出运行时错误。
(请注意,我实际上并没有编译它,所以我可能会遇到语法错误。但是大多数人不知道如何使用 Class
来转换对象。)
类型系统只知道类型,不知道对象,因此无法区分"key1"
和"key2"
,因为两者都是String
.
类型
如果键有不同的类型,最简单的方法是封装一个弱类型映射,并使用反射转换来向编译器证明类型是正确的:
class Favorites {
private Map<Class<?>,?> map = new HashMap<>();
<V> V get(Class<V> clazz) {
return clazz.cast(map.get(clazz));
}
<V> void put(Class<V> clazz, V value) {
map.put(clazz, value);
}
}
Favorites favs = new Favorites();
favs.put(String.class, "hello");
favs.put(Integer.class, 42);
favs.get(String.class).charAt(1);
所以这是一个有点棘手的问题(对我来说)。
我有一个通用对象。称它为我的对象。这个对象有一个 return 类型为 T:
的方法public class MyObject<T>
{
private T _t;
public MyObject(T t)
{
_t = t;
}
//...
public T get()
{
return _t;
}
}
(显然我的 "MyObject" 做得更多,但这就是要点)。
现在,我想要一张这种类型的地图:
Map<String, MyObject<?>> m = new HashMap<>();
我希望能够使用一些预定义的字符串名称来获取地图,并且这些地图可以是任何 MyObject。例如,我可以调用:
m.put("map_1", new MyObject<String>("String"));
m.put("map_2", new MyObject<Integer>(new Integer(3));
m.put("map_3", new MyObject<Long>(new Long(5));
等等
但是 - 这是棘手的部分 - 当我从地图中获取一些值时,我希望地图 "remember" MyObject 的参数化类型。使用
m.get("map_1");
会return一个
MyObject<Object>
类型,因为地图被定义为包含
MyObject<?>
值。因此:
m.get("map_1").get() // <-- This is an Object, not a String!
为了能够获得有关 MyObject 获取的对象的正确 - 完整 - 信息,可以进行哪些修改(如果有的话),以便调用最后一行 (m.get("map_1")) 会 return 一个
MyObject<String>
谢谢:)
阿米尔。
您可以获得 class.
Class c = m.get("map_1").get().getClass();
if (String.class.equals(c)) {
System.out.println("its a String");
}
这是一个完整的测试。
public class GenericsTest {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
Map<String, MyObject<?>> map = new HashMap<>();
MyObject<String> obj = new MyObject<>("hello");
map.put("greeting", obj);
Class c = map.get("greeting").get().getClass();
if (String.class.equals(c)) {
System.out.println("its a String");
}
}
static class MyObject<T> {
T t;
public MyObject(T t) {
this.t = t;
}
T get() {
return t;
}
}
}
来自 Joshua Bloch 的 Effective Java 的类型安全异构容器可能在这里工作。基本上你添加一个 Class
对象来表示类型。
public class MyObject<T>
{
private T _t;
private Class<T> type;
public MyObject( Class<T> type, T t)
{
_t = t;
this.type = type;
}
//...
public T get()
{
return _t;
}
public Class<T> getType() { return type; }
}
那么你可以这样做:
public <T> T get( Map<String, MyObject<?>> map, String key, Class<T> type ) {
return type.cast( m.get( key ).get() );
}
它是安全的并且可以编译,但是如果你输入错误的类型会抛出运行时错误。
(请注意,我实际上并没有编译它,所以我可能会遇到语法错误。但是大多数人不知道如何使用 Class
来转换对象。)
类型系统只知道类型,不知道对象,因此无法区分"key1"
和"key2"
,因为两者都是String
.
如果键有不同的类型,最简单的方法是封装一个弱类型映射,并使用反射转换来向编译器证明类型是正确的:
class Favorites {
private Map<Class<?>,?> map = new HashMap<>();
<V> V get(Class<V> clazz) {
return clazz.cast(map.get(clazz));
}
<V> void put(Class<V> clazz, V value) {
map.put(clazz, value);
}
}
Favorites favs = new Favorites();
favs.put(String.class, "hello");
favs.put(Integer.class, 42);
favs.get(String.class).charAt(1);