使用Java HashMap多个Type

时间:2014-11-11 17:05:00

标签: java arraylist hashmap typesafe

我使用Hashmap作为内存缓存。基本上,这就是我所拥有的:

private static final Map<String, Object> lookup = new HashMap<String, Object>();

    public static Object get(CacheHelper key) {
        return lookup.get(key.getId());
    }

    public static void store(CacheHelper key, Object value) {
        lookup.put(key.getId(), value);
    }

没关系。但对于每一个对象我都会得到#34;从地图上,我必须施展,这是非常难看的。 我想把ArrayList和许多其他不同的东西放进去。

有人知道其他解决方案是类型安全吗?

(当然,我可以为每种类型创建一个getter和setter,但这是唯一的解决方案吗?

那么,有没有更好的方法来创建内存缓存,或者有人知道如何将hashmap包装起来更安全吗?

5 个答案:

答案 0 :(得分:7)

此问题的一个解决方案是使用CacheHelper<T>使您的CacheHelper类型通用。然后为地图创建一个包装器:

class MyCache {
  private final Map<CacheHelper<?>, Object> backingMap = new HashMap<>();
  public <T> void put(CacheHelper<T> key, T value) {
    backingMap.put(key, value);
  }
  @SuppressWarnings("unchecked")
  // as long as all entries are put in via put, the cast is safe
  public <T> T get(CacheHelper<T> key) {
    return (T) backingMap.get(key);
  }
}

Java编译器实际上在内部使用这种方法;见例如here。您不必传递明确的Class个对象,但您必须知道实际与每个密钥相关联的类型,这应该是表现良好的应用

答案 1 :(得分:3)

您可以很好地存储元素的类型,将您期望的类型传递给get()方法并检查。 AFAIK没有内置的方法来存储不同类型的类型,没有一些常见的超类或接口。

基本上你这样做:

private static final Map<String, Object> lookup = new HashMap<>();
private static final Map<String, Class<?>> types= new HashMap<>();   

public static <T> T get(CacheHelper key, Class<T> expectedType ) {
  Class<?> type = types.get(key.getId());
  if( type == null || expectedType == null || expectedType.isAssignableFrom( type ) ) {
    throw new IllegalArgumentException("wrong type");
  }   
  return (T)lookup.get(key.getId());
}

//if null values should be allowed, you'd need to change the signature to use generics 
//and pass the expected type as well, e.g. <T> void store(CacheHelper key, T value, Class<T> type) 
public static void store(CacheHelper key, Object value ) {
    lookup.put(key.getId(), value);
    types.put( key.getId(), value.getClass() );
}

请注意,由于您基本上禁用了缓存中的泛型,因此仍然无法捕获任何编译时错误。如果没有共同的超类/接口或缓存本身的泛型类型,那么就没有编译时间来捕获这些错误 - 至少不容易。

如果您不想自己进行演员表演,可以将其隐藏在get()内,并使用可能的类演员例外。在这种情况下,您可以让编译器从调用中推断T的类型(通过赋值,显式设置或参数类型)。

但是,通过预期的课程可以轻松提供其他信息,您可以使用这些信息创建更好的错误消息等,而这些消息是您从ClassCastException获得的。

答案 2 :(得分:1)

您可以将Class对象作为参数传递,并使用Class.cast方法:

public static <T> T get(CacheHelper key, Class<T> clazz){
    return clazz.cast(lookup.get(key.getId());
}

答案 3 :(得分:0)

如果java版本为7(或更高版本)

,您可以使用generics功能
public static <T> T get(CacheHelper key){
    return (T)(lookup.get(key.getId());
}

然后你可以按如下方式调用它:

Myclass.<String>get(key);

在上面的例子中,我认为Myclass是一个包含get()方法的类

答案 4 :(得分:0)

get需要一个Class<T>,因为类型擦除,强制转换(T)是无意义的无操作。

要么一直包括班级;每个班级的ID:

private final Map<Class<?>, Map<String, Object>> lookup = new HashMap<>();

public <T> T get(Class<T> klass, String id) {
    Map<String, Object> mapById = lookup.get(klass);
    if (mapById == null) {
        return null;
    }
    Object value = mapById.get(id);
    return klass.cast(value);
}

public <T> void store(Class<T> klass, String id, T value) {
    Map<String, Object> mapById = lookup.get(klass);
    if (mapById == null) {
        mapById = new HashMap<>();
        lookup.put(klass, mapById);
    }
    mapById.put(id, value);
}

store不使用value.getClass()来允许get by接口或基类:

store(Number.class, "x", 3.14);
store(Number.class, "x", 3);
store(Number.class, "x", new BigDecimal("3.14"));

或只做

public static <T> T get(Class<T> klass, String id) {
    Object value = lookup.get(id);
    return klass.cast(value);
}

(我为读者简化了原始代码。)