简体   繁体   English

使用Java HashMap多个Type

[英]Use Java HashMap multiple Type

I'm using a Hashmap as my in-memory cache. 我正在使用Hashmap作为我的内存缓存。 So basically, this is what I have: 基本上,这就是我所拥有的:

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);
    }

That's fine. 没关系。 But for every Object I "get" from the Map, I have to cast, which is very ugly. 但对于我从地图中“获取”的每个物体,我必须施放,这非常难看。 I want to put ArrayList and many other different things into it. 我想把ArrayList和许多其他不同的东西放进去。

Does anybody know an other solution to be typesafe ? 有人知道其他解决方案是类型安全的吗?

(For sure, I can create for every type a getter and setter, but is that the only solution ? (当然,我可以为每种类型创建一个getter和setter,但这是唯一的解决方案吗?

So, is there a better way to create an in-memory cache or does somebody have an idea how to wrap the hashmap to be more safe ? 那么,是否有更好的方法来创建内存缓存,或者有人知道如何将hashmap包装起来更安全吗?

One solution to this problem is to make your CacheHelper type generic, with CacheHelper<T> . 解决此问题的一个方法是使用CacheHelper<T>使您的CacheHelper类型通用。 Then create a wrapper for your map: 然后为地图创建一个包装器:

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);
  }
}

The Java compiler actually uses this approach internally; Java编译器实际上在内部使用这种方法; see eg here . 这里 You don't have to pass around explicit Class objects, but you do have to know what type is actually associated with each key, which is as it should be in well-behaved applications. 您不必传递显式的Class对象,但您必须知道与每个键实际关联的类型,这与应该在行为良好的应用程序中应该相同。

You could store the type of the elements ass well, pass the type you expect to the get() method and check there. 你可以很好地存储元素的类型,将你期望的类型传递给get()方法并检查那里。 AFAIK there's no built-in way to make storing different types typesafe without some common superclass or interface. AFAIK没有内置的方法来存储不同类型的类型安全而没有一些常见的超类或接口。

Basically you'd do this: 基本上你这样做:

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() );
}

Note that this still wouldn't catch any compile time errors since you're basically disabling generics inside your cache. 请注意,由于您基本上禁用了缓存中的泛型,因此仍然无法捕获任何编译时错误。 Without a common superclass/interface or a generic type on cache itself there's no compile time way to catch those errors - at least no easy one. 如果没有常见的超类/接口或缓存本身的泛型类型,那么就没有编译时间来捕获这些错误 - 至少不容易。

If you just don't want to do the casts yourself you can hide them inside get() and live with the possible class cast exception. 如果你只是不想自己进行强制转换,你可以将它们隐藏在get()并使用可能的类强制转换异常。 In that case you can let the compiler infer the type of T from the call (by assignment, explicitly set or type of the parameter). 在这种情况下,您可以让编译器从调用中推断出T的类型(通过赋值,显式设置或参数类型)。

However, passing the expected class easily provides additional information that you can use to create better error messages etc. which you'd not get from a ClassCastException . 但是,传递期望的类很容易提供额外的信息,您可以使用它们来创建更好的错误消息等,这些消息是您从ClassCastException获得的。

You can pass a Class object as a parameter and use the Class.cast method : 您可以将Class对象作为参数传递,并使用Class.cast方法:

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

You can use generics feature if java version is 7 (or upper) 如果java版本为7(或更高版本),您可以使用generics功能

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

And you can then call it as follows : 然后你可以按如下方式调用它:

Myclass.<String>get(key);

In the above example I supposed the Myclass be a class containing the get() method 在上面的例子中,我认为Myclass是一个包含get()方法的类

The get needs a Class<T> , as because of type erasure a cast (T) is a senseless no-op. get需要一个Class<T> ,因为类型擦除,一个演员(T)是一个无意义的无操作。

Either incorporate the Class all the way; 要么一直包括班级; IDs per class: 每班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);
}

The store not using value.getClass() to allow a get by interface or base class: store不使用value.getClass()来允许get by接口或基类:

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

Or do only 或者只做

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

(I simplified the original code a bit, for the readers.) (我为读者简化了原始代码。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM