10

我有一个 POJO 指定为:MyClass<U>,其中U是泛型类型参数。我正在尝试编写一个接受类引用Class<T>并填充类型映射的实用程序方法Map<String, T>(接受要填充的映射)。

该方法的实现如下:

static void populateMap(Map<String, T> map, Class<T> type) {

    ...

    // Parses into the specified type and returns an object of that type.
    T obj = parse(..., type);
    map.put (key, obj);
    ...

    return map;
}

这编译得很好。在我的调用者中,我尝试使用任何MyClass实例(无论类型)作为值填充映射。因此我使用以下代码:

// Loses type information
Map<String, MyClass<?>> m = new HashMap<>();
populateMap(m, MyClass.class);

这不编译。编译错误:

类型 ... 中的方法populate(Map<String,T>, Class<T>)不适用于参数(Map<String,MyClass<?>>, Class<MyClass>)

我怎样才能解决这个问题?

4

2 回答 2

5

在这种情况下,对 进行未经检查的强制转换应该是安全的Class<MyClass<?>>

// This is okay because we're switching to a type with an unbounded wildcard -
// the behaviors of Class.newInstance and Class.cast are still safe.
@SuppressWarnings("unchecked") 
Class<MyClass<?>> classWithNarrowedType =
        (Class<MyClass<?>>)(Class<?>)MyClass.class;
populateMap(m, classWithNarrowedType);

这是一个笨拙的解决方案,尤其是如果您有许多这样的调用站点,但是无法回避使用原始类型对类文字进行参数化的事实,这使得它们用作参数化类型的工厂MyClass<T>本来就很尴尬。

一个可能更清洁的解决方案将与populateMap类文字的使用脱钩:

interface Parser<T> {

    T parse();
}

static void populateMap(Map<String, T> map, Parser<T> parser) { ... }

...

Map<String, MyClass<?>> m = new HashMap<>();
Parser<MyClass<?>> myClassParser = new Parser<MyClass<?>>() {
    @Override
    public MyClass<?> parse() {
        return parse(..., MyClass.class);
    }
};
populateMap(m, myClassParser);

顺便说一句,我建议使用更灵活的签名(有关更多信息,请参阅什么是 PECS(生产者扩展消费者超级)?):

static void populateMap(Map<String, ? super T> map, Parser<T> parser)
于 2013-09-22T07:11:03.787 回答
2

由于类型擦除,不存在表示泛型类型的 Class 对象,您只能使用原始类型,例如 MyClass(没有泛型参数)。

一种可能的解决方法非常难看:将 m 声明或强制转换为Map<String, MyClass>并准备面对警告和错误的海啸(错误可以通过强制转换来修复,并变成多个警告)。
有关可能更好的解决方法,请参阅 Paul 的回答 :)

另请参阅使用泛型获取 java.lang.Class

于 2013-09-22T07:03:40.030 回答