【问题标题】:Restrict a generic Class parameter to classes that implement Map将泛型 Class 参数限制为实现 Map 的类
【发布时间】:2019-02-18 10:57:56
【问题描述】:

我正在尝试编写一个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 的类?

【问题讨论】:

  • 为什么不改用Supplier?这样,用户就可以通过LinkedHashMap::new,而不必担心反射。

标签: java generics


【解决方案1】:

使用Supplier 而不是Class

public MapBuilder(Supplier<? extends Map<K, V>> supplier) {
    map = supplier.get();
}

然后可以这样调用:

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

这也更安全,因为Class&lt;Map&gt; 可能没有默认构造函数,这会引发错误(响应速度不快的代码)

【讨论】:

  • 你可以完全去掉通配符:public MapBuilder(Supplier&lt;Map&lt;K, V&gt;&gt; supplier)
  • @ernest_k 你不能。如果没有通配符,如果您提供例如class Sup implements Supplier&lt;HashMap&lt;Integer, Integer&gt;&gt; 的实例,它将失败。这是一个非常罕见的情况,但它可能会发生
  • 同意。即使您使用 lambda(Supplier&lt;HashMap&lt;String, Integer&gt;&gt; supp = HashMap::new; MapBuilder&lt;String, Integer&gt; mb = new MapBuilder&lt;String, Integer&gt;(supp); - 但这是我个人很高兴没有支持的东西(这是一种意见),它仍然会失败。这是一个很好的答案,顺便说一句。
  • @ernest_k 谢谢 :),我同意虽然在一个小项目中它不会产生影响(使用或不使用通配符)但是在编写跨不同项目使用的 API 时,它是让使用您的 API 的人们的生活更加轻松
【解决方案2】:

以下将起作用:

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

【讨论】:

  • 会产生未经检查的警告
【解决方案3】:

问题是LinkedHashMap.class

Class<LinkedHashMap>

而不是像

Class<LinkedHashMap<Integer, String>>

这些也是不可转换的类型(所以你不能转换它)并且没有办法获得后者的实例。

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

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

泛型在运行时被删除,因此在运行时所有Maps 的行为都与Map&lt;Object, Object&gt; 一样。因此,您构建的类是否使用原始类型并不重要。


顺便说一句,Class::newInstance 已被弃用。使用

mapType.getConstructor().newInstance()

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-11-17
    • 1970-01-01
    • 1970-01-01
    • 2012-07-19
    • 1970-01-01
    • 2021-06-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多