【问题标题】:Is there a way to store "extensible enums" in an EnumMap?有没有办法在 EnumMap 中存储“可扩展枚举”?
【发布时间】:2023-03-31 02:53:01
【问题描述】:

我指的是 Joshua Bloch 在 Effective Java 中的 Item 34 中的范例。我想采用他使用的方法,即让每个相关的enum 实现一个基本接口,并从“子枚举”中初始化一个EnumMap。请参阅下面的代码部分。我收到一个我不理解的语法错误。我没有设置这种实现方法,但我想了解它为什么行不通。

请注意,此示例假定每个类定义都在其自己的文件中。

public interface BaseEnum { 
    ... 
}

public enum EnumOps1 implements BaseEnum { 
    ... 
}

public class Widget {
    public Widget() {
         regMap = new EnumMap<EnumOps1, WidgetData>(EnumOps1.class);

         for (EnumOps1 op : EnumOps1.values()) {
             regMap.put(op, getWidgetData(op.key()));  // line with syntax error
         }
    }

    protected Map<? extends BaseEnum, WidgetData> regMap;
} 

语法错误详情:

接口 java.util.Map&lt;K,V&gt; 中的方法不能应用于给定类型
必需:捕获#1 of ?扩展 BaseEnum、WidgetData
找到:EnumOps1,WidgetData

【问题讨论】:

    标签: java interface enums


    【解决方案1】:

    你的通配符有问题。您应该将您的地图声明为Map&lt;BaseEnum, WidgetData&gt;,并将您的 EnumMap 声明为 ex。 HashMap&lt;BaseEnum, WidgetData&gt;

    关于为什么在 SO 上这是真的有很多讨论,但请参阅 What is PECS (Producer Extends Consumer Super)? 示例。

    编辑

    很遗憾,你是对的 - 你不能在那里使用 EnumMap。这是因为您尝试使用一个接口,而 EnumMap 规定(因为它需要一个类型 T extends Enum&lt;T&gt;)它必须是一个 Enum。

    您的选择基本上可以归结为

    1) 使用 EnumMap&lt;EnumOps1,...&gt; 并失去多态性

    2) 使用HashMap&lt;BaseEnum,...&gt; 一切正常,但您必须使用非枚举映射。

    3) 尝试使用通配符,但您会遇到我之前链接的 PECS 限制,例如你可以添加或删除元素,但不能同时添加(super vs extends

    【讨论】:

    • 当我尝试该声明和实例化时,我得到另一个语法错误:类型参数 BaseEnum 不在其范围内。该错误在实例化行上(regMap = ...)有什么想法吗?
    • 我同意您的回答的正确性。但是,尚不完全清楚为什么该特定行会引发错误。在实例化或调用 EnumMap 的所有情况下,都会将枚举传递给它。如果我遗漏了一些明显的东西,我深表歉意。
    • 代码与原帖相同。我希望更好地理解当 EnumMap 像我一样实例化时将枚举传递给 EnumMap.put 方法有什么问题。我了解您列出的其他选项也可以使用。
    • 问题是你的 Map 键的类型是? extends BaseEnum,这意味着regMap 变量的键是BaseEnum一些未指定的子类。向它添加元素是从不安全的,因为基于静态类型(这是一些未指定的子类型),您无法知道必须添加到集合中的特定子类型.如果你弄错了,你将违反类型安全并且编译器无法验证它,所以它不允许它。这有意义吗?
    • 是的,这有帮助。 EnumOps1 与 BaseEnum 不同,因为泛型不是协变的,因此编译器会抱怨。让我失望的是书中的例子。这些示例是正确且良好的,但类型在编译时始终是静态/已知的。它只是以一种巧妙的方式完成。感谢您的努力!
    【解决方案2】:

    这是我经过数小时的研究和尝试后所做的事情。我想获得 EnumMap 的好处,而不是仅限于一个枚举类,而是许多实现相同接口的不同枚举类。 在下面的示例中,我有一个名为 Layout 的接口,我有几个枚举类对其进行了扩展。然后我创建了一个帮助类 Record,它利用 EnumMap 来处理和存储将一些 String 值映射到我的枚举类中的枚举,我需要它来接受所有扩展 Layout 接口的不同枚举类。所以我创建了以下类(以下简称为给出一个想法):

    public class Record<T extends Enum<T> & Layout>
    {
        private Map<T, String> fields;
    
        /**Constructor accepting the class name of one of the enums implementing layout
        */
        public Record(Class<T> layout_type)
        {
            fields = new EnumMap<T, String>(layout_type);
            for (T type : layout_type.getEnumConstants())
            {
                fields.put(type, "");
            }
        }
    
        /*Here's an example of a method that manipulates (adds or replaces) an enum map  
        *item and its corresponding String value in the enum map
        *This won't warn any unchecked exceptions or anything and I can pass any
        *enum from my enum class that implements layout enum.NAME 
        */
        public void setRecordFieldValue(final T item, String value)
        {
            fields.put(item, value);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-17
      • 2014-09-11
      • 1970-01-01
      • 1970-01-01
      • 2016-07-24
      相关资源
      最近更新 更多