将通用类参数限制为实现Map接口的类

8

我正在尝试编写一个Map构建器。其中一个构造函数将允许客户端指定他们希望构建的Map类型。

public class MapBuilder<K, V> {

    private Map<K, V> map;

    /**
     * Create a Map builder
     * @param mapType the type of Map to build. This type must support a default constructor
     * @throws Exception
     */
    public MapBuilder(Class<? extends Map<K, V>> mapType) throws Exception {
        map = mapType.newInstance();
    }

    // remaining implementation omitted
}

目的是可以使用以下方法构建建造者的实例:

MapBuilder<Integer, String> builder = new MapBuilder<Integer, String>(LinkedHashMap.class);

或者

MapBuilder<Integer, String> builder = new MapBuilder<Integer, String>(HashMap.class);

看起来构造函数参数的类型签名目前不支持此项功能,因为上面的代码会导致“无法解析的构造函数”编译错误。

我该如何更改我的构造函数,以便仅接受实现了Map的类?


4
为什么不使用Supplier呢?这样,用户可以传递LinkedHashMap::new,而不必担心反射。 - RealSkeptic
3个回答

10
使用Supplier取代Class
public MapBuilder(Supplier<? extends Map<K, V>> supplier) {
    map = supplier.get();
}

那么这样就可以这样调用:

MapBuilder<Integer, Integer> builder = new MapBuilder<>(LinkedHashMap::new);

这也更安全,因为 Class<Map> 可能没有默认构造函数,这将抛出错误(这不是非常响应的代码)


1
你可以完全摆脱通配符:public MapBuilder(Supplier<Map<K, V>> supplier) - ernest_k
1
@ernest_k 你不能这样做。如果没有通配符,当你提供一个例如 class Sup implements Supplier<HashMap<Integer, Integer>> 的实例时,它会失败。这是一个非常罕见的情况,但确实可能发生。 - Lino
1
同意。即使您使用lambda表达式(Supplier<HashMap<String, Integer>> supp = HashMap::new; MapBuilder<String, Integer> mb = new MapBuilder<String, Integer>(supp);),它仍将失败 - 但这是我个人很乐意不支持的事情(这是一种观点)。顺便说一句,这是一个好答案。 - ernest_k
2
@ernest_k 谢谢 :),我同意在小项目中使用通配符与否不会有什么区别,但是当编写一个在不同项目中使用的API时,这是正确的方式,可以让使用您的API的人们的生活变得更加轻松。 - Lino

2
以下方法可行:

最初的回答:

public MapBuilder(Class<? extends Map> mapType) throws Exception {
    map = mapType.newInstance();
}

1
虽然会产生未经检查的警告 - Lino

2
问题在于LinkedHashMap.class是一个最初的回答。
Class<LinkedHashMap>

最初的回答
而不是像这样的东西
Class<LinkedHashMap<Integer, String>>

这些也是不可转换的类型(因此您无法强制转换),而且没有办法获得后者的实例。

您可以做的是更改构造函数为

最初的回答:

这些类型是不可转换的(因此不能进行类型转换),也没有方法获取后者的实例。

您可以更改构造函数为

public MapBuilder(Class<? extends Map> mapType) throws Exception

泛型在运行时会被擦除,因此在运行时所有的Map都会像Map<Object, Object>一样。因此,使用原始类型构造的类并不重要。


顺便说一下,Class::newInstance已经被弃用了。使用

mapType.getConstructor().newInstance()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接