26

我不确定我想做什么是可能的,但如果是的话,我想知道怎么做。基本上,我想创建一个 Map,其中键是一个类 ( java.lang.Class),该条目的值是该类的一个实例。目前我有

private Map<Class<?>, Object> myMap = new HashMap<Class<?>, Object>();

但是,这意味着任何对象都可以放置在地图中。如果可能的话,我想做,所以只有key中的类的一个实例可以放在map中。有什么方法可以使用吗?对类进行参数化以确保这一点?

此外,我发现在执行此类操作时可能存在内存泄漏。我不确定我是否完全理解这是如何发生的。我只会将单例对象粘贴到地图中,所以还会担心内存泄漏吗?如果是这样,我该如何预防?

4

5 回答 5

17

Java 的类型系统根本不足以强制执行您直接描述的类型约束,并且您需要执行一些不安全的强制转换才能使其工作 - 或者将其包装Map在其他一些强制类型安全的 API 中。 Guava ClassToInstanceMap正是针对这个用例做到了这一点,它提供了一个外部安全的 API,对Map接口施加了额外的限制以使其工作。(披露:我为 Guava 做出了贡献。)

唯一可能导致内存泄漏的情况是,如果您在这里使用的某些类不会在应用程序的生命周期内保留。这不是很多用户关心的问题,特别是如果您正在编写一个不太关心卸载未使用的类的“服务器端”应用程序。

于 2012-04-17T16:07:32.830 回答
14

您使用的是异构容器。这些可以通过使用类型标记(就像你已经做的那样)和Class.cast()正确的应用程序逻辑使它们成为类型安全的:所以在 内部有一个未经检查的 suppressWarning Class.cast(),但应用程序逻辑保证了正确性。

我建议您阅读 Josh Bloch 的 Effective Java Item 29:考虑类型安全的异构容器。他的例子是:

// Typesafe heterogeneous container pattern - implementation
public class Favorites {
  private Map<Class<?>, Object> favorites =
    new HashMap<Class<?>, Object>();

  public <T> void putFavorite(Class<T> type, T instance) {
    if (type == null)
      throw new NullPointerException("Type is null");
    favorites.put(type, instance);
  }

  public <T> T getFavorite(Class<T> type) {
    return type.cast(favorites.get(type));
  }
}

如果您拥有实际上不再需要的任意许多不同类型,我只会看到内存泄漏的可能性。

于 2012-04-17T16:24:53.470 回答
10

您可以隐藏集合,只允许通过访问器方法访问它。

private final Map<Class, Object> myMap = new HashMap<Class, Object>();

public <T> void putMap(Class<T> tClass, T t) {
     myMap.put(tClass, t);
}

@SuppressWarnings("unchecked")
public <T> T getMap(Class<T> tClass) {
    return (T) myMap.get(tClass);
}

可以忽略该警告,因为您知道最后一个方法中的强制转换是安全的,即使编译器不这样做。

于 2012-04-17T16:13:20.830 回答
0

由于类型擦除,这是不可能的。

但是,您可以子类HashMap化并将一些逻辑写入put将为您执行此操作的方法:

public void put(K key, V value) {

    if  ( // value is an instance of key using .getClass()) { 
        super.put(key, value)
    }
    throw new Exception();
}

(代码仅用于说明目的)

于 2012-04-17T16:09:25.060 回答
0

我有同样的要求。我使用以下代码解决了它:

interface Service { }

class ServiceCache {
private HashMap<Class<? extends Service>, Service> services;

public ServiceCache() {
    services = new HashMap<>();
}

public <T extends Service> T getService(Class<T> service) {
    if (services.containsKey(service))
        return (T) services.get(service);
    return null;
}

public <T extends Service> void addService(T service, Class<? extends Service> serviceInterface) {
    services.put(serviceInterface, service);
 }
}
于 2018-06-26T13:48:54.330 回答