【问题标题】:Adding Generic member variable in Generic Class在泛型类中添加泛型成员变量
【发布时间】:2015-03-31 11:54:07
【问题描述】:

伙计们,

有什么简单的方法可以在非泛型类中添加泛型类。

基本上,缓存管理器将拥有 Cache 类的映射,该映射使用适当的泛型实现。

但是在下面的类中,我们通过 get 方法返回(getCache 方法)缓存,它需要在调用者位置显式转换如何避免它。

例如

public class CacheManager {
    private Map<String, Cache<?,?>> cacheMap = new HashMap<String, Cache<?,?>>();
    public Cache<?,?> getCache(String cacheName) {
        return cacheMap.get(cacheName);
    }

    public void addCache(String cacheName,Cache<?,?> cache) {
        cacheMap.put(cacheName, cache);
    }
}

【问题讨论】:

  • 如果没有未经检查的强制转换(在这种特殊情况下是类型安全的 IMO),就无法做到这一点。您可以通过向通配符添加边界来增加类型安全性。
  • 是否存在通配符类型,因为单个 CacheManager 实例将存储许多不同类型的缓存? (即为什么它不是 CacheManager?)
  • Rohit 能否举例说明如何为通配符添加更多边界。我现在正在做显式转换 (Cache)cacheManager.getCache("test") Erik,你是对的,我有不同的缓存,所以我不能将泛型用于 CacheManager。请提出更好的建议。
  • 您可以将cacheMap 成员的类型参数设为Object (Map&lt;String,Cache&lt;Object,Object&gt;&gt;)。然后编译器应该停止抱怨,但显然,这个解决方案有code smell

标签: java generics


【解决方案1】:

简短回答:没有(据我所知)。

这里的问题是您所做的在 Java 中根本不是类型安全的。看看这个例子:

import java.util.*;

class ClassCast {
    public static void main(String[] args) {
        HashMap<String, Pair<?, ?>> map = new HashMap<>();

        map.put("test", new Pair<String, Integer>("Hello", 5));

        Pair<Double, Double> pair = (Pair<Double, Double>) map.get("test");
    }
}

class Pair<T,V> {
    T a;
    V b;

    Pair(T a, V b) {
        this.a = a;
        this.b = b;
    }
}

你会期望这里有一个ClassCastException,但它编译并运行得非常好。原因是Pair&lt;String, Integer&gt;Pair&lt;Double, Double&gt; 的实际类实际上只是Pair(在类型擦除之后)。

要获得类型安全,您必须实现“Typesafe 异构容器模式”(Josh Bloch 在 Effective Java 中有详细解释)。简而言之,您必须在地图的键中包含类型参数。根据您的需要,您也许可以直接使用一个类作为键,否则您可能需要创建一个键对象。

示例实现:

public class CacheManager {
    private Map<MultiKey, Cache<?,?>> cacheMap = new HashMap<>();

    @SuppressWarnings("unchecked")
    public <T,V> Cache<T,V> get(String name, Class<T> t, Class<V> v) {
        // Type-safe since types are encoded in key(i.e. map will not
        // return something with the wrong type), and key is type-checked
        // on insertion.
        return (Cache<T,V>) cacheMap.get(new MultiKey(name, t, v));
    }

    public <T,V> void put(String name, Class<T> t, Class<V> v, Cache<T,V> cache) {
        cacheMap.put(new MultiKey(name, t, v), cache);
    }

    class MultiKey {
        Object[] keys;
        Integer hash = null;

        MultiKey(Object... keys) {
            this.keys = keys;
        }

        @Override
        public int hashCode() {
            if (hash == null) hash = Arrays.hashCode(keys);
            return hash;

        }

        @Override
        public boolean equals(Object o) {
            if (o == null || !(o instanceof MultiKey)) return false;
            return Arrays.equals(keys, ((MultiKey) o).keys);
        }
    }
}

示例用法:

CacheManager mng = new CacheManager();

mng.addCache("SI", String.class, Integer.class, new Cache<String, Integer>());

Cache<String, Integer> cache = mng.getCache("SI", String.class, Integer.class);

System.out.println(cache);

它并不漂亮,但它实际上是类型安全的。但可以根据实际情况进行改进,因此您不应按原样使用此代码。例如,如果您可以从 Cache 对象中获取类型,则不需要 addCache 中的 Class 参数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-09-28
    • 2014-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-21
    • 1970-01-01
    相关资源
    最近更新 更多