【问题标题】:Generics map key should allow two classes only and value one class泛型映射键应该只允许两个类并且值一个类
【发布时间】:2019-01-18 23:38:19
【问题描述】:

使用泛型我想让我的方法参数化以接受键映射(A 或 B)和值作为模型,然后它转换为 Map<UUID, Model> 的映射

问题是我在entry.getKey().getId() b/c 上遇到错误。我还没有定义什么是?关键。有人可以帮忙吗?

public void method(Map<?, Model> map) {
  Map<UUID, Model> transformation =
      map.entrySet().stream()
          .collect(
              Collectors.toMap(entry -> entry.getKey().getId(), entry -> entry.getValue()));
}

class S {
  protected UUID getId() {
    // return UUID
  }
}

class A extends S {}

class B extends S {}

class Model {
  // bla bla
}

【问题讨论】:

  • 您可以通过实现自定义类型来解决这个问题:Either&lt;A, B&gt;(或从某个库中获取一个)。它会在一定程度上影响您的类型签名,但最终实际上会给您带来类型安全的好处。另一方面,我认为在实现字典中没有实际用途,其中键可以是两个完全不相关的事物之一,我宁愿为此使用两个单独的映射。
  • ... 多读一点(我肯定是瞎子!),你确实有一个共同的类型S。然后按照答案的建议,使用? extends S
  • @M.Prokhorov 即使没有通用类型,侵入性较小的解决方案是为提取 UUID 的方法提供功能参数:method(mapOfA, A::getId)
  • @AndyTurner,是的,他只是“解释”了转换方法如何通过添加函数参数从任何键类型中获取UUID

标签: java generics


【解决方案1】:

如果AB 实现提供getId() 方法的通用接口,则将通配符与该方法绑定:

public void method(Map<? extends S, Model> map) {

【讨论】:

  • 或者如果不存在就添加接口
  • @StoyanRadnev 确实如此。
【解决方案2】:

因为AB 都扩展了S,所以map 的通用键可以简单地为S

public void method(Map<? extends S, Model> map) {

除了创建AB 都实现的一些接口之外,您目前无法限制S 的其他孩子成为映射中的键(直到Java 获得密封类),并且然后将该接口用作map 的通用键。

【讨论】:

  • 请注意,这不会接受 Map&lt;A, Model&gt; 作为参数。
  • 他可以将Either&lt;A, B&gt; 作为自定义类型来模拟密封集合,但这需要在地图实现中使用一些自定义代码。
  • @M.Prokhorov 你能帮忙定制实现吗?
  • @d-man,你需要自定义实现吗?我会亲自去寻找答案中的内容,除非您确实需要模拟密封类型。但是如果不严格重写代码,您就无法在其中添加第三种类型。
最近更新 更多