有没有办法在EnumMap中存储“可扩展的枚举”?

时间:2010-10-19 23:31:31

标签: java interface enums

我指的是Joshua Bloch在Effective Java的第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中放置的

方法<K,V>不能应用于给定的类型
    要求:捕获#1?扩展BaseEnum,WidgetData
    发现:EnumOps1,WidgetData

2 个答案:

答案 0 :(得分:4)

这是你的通配符问题。您应该将地图声明为Map<BaseEnum, WidgetData>,并将您的EnumMap声明为ex。 HashMap<BaseEnum, WidgetData>

有很多关于为什么在SO上如此的讨论,但请参阅What is PECS (Producer Extends Consumer Super)?

修改

可悲的是,你是对的 - 你不能在那里使用EnumMap。这是因为您正在尝试使用接口,并且EnumMap规定(因为它需要类型T extends Enum<T>)它必须只是一个枚举。

您的选择基本归结为

1)使用EnumMap<EnumOps1,...>并失去多态性

2)使用HashMap<BaseEnum,...>,一切正常,但你必须使用非枚举地图。

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

答案 1 :(得分:1)

这是我经过数小时研究和尝试后所做的事情。我想要获得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);
    }
}