【问题标题】:Generic Enum with generic type Enum具有泛型 Enum 的泛型 Enum
【发布时间】:2017-01-18 21:36:14
【问题描述】:

我有以下接口和类定义。

interface A
{
}

class B
{
    public static enum C implements A
    {
        c1, c2, c3;
    }

    public static enum D implements A
    {
        d1, d2, d3;
    }

    public static enum E implements A
    {
        e1, e2, e3;
    }
}

现在我有一个类,我在其中声明一个 Map 并将枚举分配为键并设置一个值。

class Test
{
    private Map<C, String> myMap;

    public void assignVal()
    {
        myMap = new EnumMap<C, String>(C.class);
        myMap.put(C.c1, String.valueOf(1));
    }
}

问题:如您所见,myMap 与枚举 C 相关联。我想创建 myMap 的通用版本,因此我可以在 B 类中分配任何枚举值。

我已经浏览了 stackoverflow 帖子: How to implement enum with generics?

【问题讨论】:

  • 为什么不直接使用Map&lt;A, String&gt; 和new HashMap&lt;&gt;()?
  • @Andreas 也许A 有其他实现。

标签: java


【解决方案1】:

EnumMap 无法做到这一点。

EnumMap 需要单一类型的键 - 构造函数通过将泛型类型绑定到具体类型来强制执行此操作。

在底层,它构建了一个可能的键值缓存,用于强制执行运行时类型安全。

如果要允许层次结构中的键,则需要使用另一种类型的映射。

【讨论】:

    【解决方案2】:

    试试这个:

    private Map<Enum<? extends A>, String> myMap;
    

    这将类型绑定到任何实现 A 的 Enum。

    这相当于&lt;? extends Enum&lt;?&gt; &amp; A&gt;,但更容易输入。

    【讨论】:

    • 真的吗?不是? extends Enum&lt;?&gt; &amp; A?由于Enum 的界限,这看起来不起作用。
    • @andy 他们是一样的:见stackoverflow.com/questions/14122783/…
    • 但是你能把一个有上限键类型的地图放进去吗?
    • 不能将put() 与Map&lt;? extends Enum&lt;? extends A&gt;, String&gt;。只需更改为Map&lt;Enum&lt;? extends A&gt;, String&gt;。 --- 当然,这还不够,因为myMap = new EnumMap&lt;C, String&gt;(C.class) 不起作用。那必须是myMap = new HashMap&lt;&gt;()。
    • @Andreas 就像我说的,这意味着键不是A 类型。例如,A a = myMap.keySet().iterator().next(); 是编译错误。
    【解决方案3】:

    我会支持 teppic 所说的——你不能将EnumMap 与A 的所有子类一起使用。它必须使用特定的具体类来构造。参见 EnumMap 的 JavaDoc

    https://docs.oracle.com/javase/7/docs/api/java/util/EnumMap.html

    我看到你这里似乎有两个选择

    使用其他类型的地图实现(例如HashMap)

    Map<A, String> myMap1 = new HashMap<>();
    myMap1.put(C.c1, C.c1.name());
    System.out.println(myMap1);
    

    输出将是:

    {c1=c1}
    

    实现A 的另一个子类,以便您可以使用EnumMap

    如果出于某些原因,你真的想使用EnumMap,并且你不 想在一个枚举中实现C、D、E 的所有值,你还有 以下解决方案。

    实现A 的新子类(在下面的代码示例中我们称之为SuperCde),它具有C、D 和E 的所有可能值,并且 它有一个静态方法getSuperCde() 作为桥梁:

    public static enum SuperCde implements A{
        c1,c2,c3,
        d1,d2,d3,
        e1,e2,e3
        ;
    
        public static SuperCde getSuperCde(A a) {
            if (a instanceof C) {
                C cValue = (C) a;
                switch (cValue) {
                case c1: return SuperCde.c1;
                case c2: return SuperCde.c2;
                case c3: return SuperCde.c3;
                default: throw new IllegalArgumentException();              
                }
            } else if (a instanceof D) {
                D dValue = (D) a;
                switch (dValue) {
                case d1: return SuperCde.d1;
                case d2: return SuperCde.d2;
                case d3: return SuperCde.d3;
                default: throw new IllegalArgumentException();              
                }
            } else if (a instanceof E) {
                E eValue = (E) a;
                switch (eValue) {
                case e1: return SuperCde.e1;
                case e2: return SuperCde.e2;
                case e3: return SuperCde.e3;
                default: throw new IllegalArgumentException();              
                }
            } else {
                throw new IllegalArgumentException();
            }
        }
    }
    

    然后就可以使用下面的代码了:

    Map<SuperCde, String> myMap2 = new EnumMap<SuperCde, String>(SuperCde.class);       
    myMap2.put(SuperCde.getSuperCde(C.c1), C.c1.name());
    System.out.println(myMap2);
    

    输出将是:

    {c1=c1}
    

    【讨论】:

      【解决方案4】:

      正如其他人指出的那样,您不能使用EnumMap。此外,虽然使用 &lt;E extends Enum&lt;E&gt; &amp; A&gt; 有效,但有界通配符 &lt;? extends Enum&lt;?&gt; &amp; A&gt; 的版本不存在。鉴于此,您可以使用 &lt;E extends Enum&lt;E&gt; &amp; A&gt; 构造进行静态类型检查的对象包装您的映射键。

      class EnumAWrapper{
          final Enum<?> enumObj;
          final A aObj;
      
          <E extends Enum<E> & A> EnumAWrapper(E enumA){
              enumObj = enumA;
              aObj = enumA;
          }
      
          @Override
          public int hashCode() {
              final int prime = 31;
              int result = 1;
              result = prime * result + ((aObj == null) ? 0 : aObj.hashCode());
              return result;
          }
      
          @Override
          public boolean equals(Object obj) {
              if (this == obj)
                  return true;
              if (obj == null)
                  return false;
              if (getClass() != obj.getClass())
                  return false;
              EnumAWrapper other = (EnumAWrapper) obj;
              if (aObj == null) {
                  if (other.aObj != null)
                      return false;
              } else if (!aObj.equals(other.aObj))
                  return false;
              return true;
          }
      
      }
      

      这是一个演示:

      Map<EnumAWrapper, String> map = new HashMap<>();
      
      //does compile
      map.put(new EnumAWrapper(C.c1), "");
      map.put(new EnumAWrapper(D.d1), "");
      map.put(new EnumAWrapper(E.e1), "");
      A aObj = map.keySet().iterator().next().aObj;
      Enum<?> enumObj = map.keySet().iterator().next().enumObj;
      
      //does not compile (given that enum F does not implement A)
      map.put(new EnumAWrapper(new A(){}), "");
      map.put(new EnumAWrapper(F.f1), "");
      

      【讨论】:

        猜你喜欢
        • 2013-09-15
        • 1970-01-01
        • 1970-01-01
        • 2023-03-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多