如何使用Java Generics以便为返回类型为List <interface> </interface> </interfaceimpl>的方法返回List <interfaceimpl>

时间:2014-10-11 14:52:01

标签: java generics enums

我正在尝试建模一个可以包含子菜单的菜单的菜单系统。我想要一个通用的interface用于从菜单和子菜单中通过其父菜单检索选项,我在设计它时遇到了麻烦,同时保持其类型安全,灵活且没有编译时“不兼容类型”错误。我试图通过使用Java泛型为简单菜单指定Menu interface和为父菜单指定ParentMenu interface来实现我的目标,父菜单使用方法参数和返回类型的泛型类型参数。

我有第三个Enumerable interface用于表示特定菜单上的可用选项。 implement Enumerable interface所有enum classes的类都列举了菜单上的选项。父菜单可以容纳许多子菜单,我想要一个通用的单一方法,允许父菜单类的客户端获取其任何一个子菜单的选项。

父菜单具体class有一个方法,其签名指定返回值List<Enumerable>。我希望能够返回List<MenuOption> MenuOption implements Enumerable interface的{​​{1}}。但是,我收到一条错误消息,指出我要返回的List<MenuOption>List<Enumerable>不兼容。

基本Menu interface

package example;

import java.util.List;

public interface Menu<T> {
    public List<T> getOptions();
}

ParentMenu interface

package example;

import java.util.List;

public interface ParentMenu<T,U> {
    public List<T> getSubMenuOptions(U subMenu);
}

`可枚举的界面:

package example;

public interface Enumerable {
    public String getName();
}

以下是Menu interface

的实现
package example;

import java.util.*;

public class SimpleMenu implements Menu<MenuOption> {

    private final Map<MenuOption, String> menuLinks;

    public SimpleMenu() {
        menuLinks = new HashMap<>();
        for(MenuOption option : MenuOption.values()) {
            //Totally arbitrary mapping to avoid adding extra classes and Selenium stuff that is not actually part of
            //the question I'm asking.
            menuLinks.put(option, option.getName());
        }
    }

    public final List<MenuOption> getOptions() {
        Set keys = menuLinks.keySet();
        List<MenuOption> options = new ArrayList<MenuOption>(keys);

        return options;
    }
}

以下enum class枚举SimpleMenu上的选项:

package example;

import java.util.HashMap;
import java.util.Map;

public enum MenuOption implements Enumerable {

    OPTION_ONE("Option One"),
    OPTION_TWO("Option Two");

    private static final Map<String, MenuOption> lookup = new HashMap<>();

    private final String name;

    static {
        for (MenuOption option : MenuOption.values()) {
            lookup.put(option.toString(), option);
        }
    }

    private MenuOption(final String name) {
        this.name = name;
    }

    public final static MenuOption getByName(final String name) {
        return lookup.get(name);
    }

    public final String getName() {
        return name;
    }
}

以下是ParentMenu interface的实现,其中包含SimpleMenu作为子菜单。以下返回语句是我仍然收到“不兼容类型”错误的原因,因为List<Enumerable>List<MenuOption>不兼容。

package example;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public final class ConfigureMenu implements ParentMenu<Enumerable,ConfigureMenu.ConfigureSubMenu> {

    public static enum ConfigureSubMenu implements Enumerable {

        SIMPLE_MENU("An Option");

        private final String name;

        private static final Map<String, ConfigureSubMenu> lookup = new HashMap<>();

        static {
            for (ConfigureSubMenu subMenu : ConfigureSubMenu.values()) {
                lookup.put(subMenu.toString(), subMenu);
            }
        }

        private ConfigureSubMenu(final String name) {
            this.name = name;
        }

        public final String getName() { return name; }

        public static ConfigureSubMenu getByName(String name) {
            return lookup.get(name);
        }
    }

    public final List<Enumerable> getSubMenuOptions(final ConfigureSubMenu subMenu) {
        switch(subMenu) {
            case SIMPLE_MENU:
                List<MenuOption> options = new SimpleMenu().getOptions();
                //This line causes the incompatible types error (List<Enumerable> is not compatible with List<MenuOption>)
                return options;
            default:
                return new ArrayList<>();
        }
    }
}

如何更改我的通用interfaces的签名或我的具体实现的签名,以便我可以摆脱此错误,仍然有一个灵活的通用方法,可以返回任何子菜单的选项包含在父菜单中?

注意:我已经多次编辑过这个问题,因为它被提问和回答是为了提高质量,并清楚地表明我的问题和我希望的目标是什么。

2 个答案:

答案 0 :(得分:3)

Java泛型可能有点棘手。您的方法返回List<Enumerable>,这意味着&#34;一个列表,其中完全符合类型Enumerable &#34;。您将返回List<MenuOption>不是相同的类型。相反,您应该在ParentMenu中将getSubMenuOptions定义为public List<? extends T> getSubMenuOptions(U subMenu);,其中List<? extends T>表示&#34;一个List,其中包含一些实现T的类型(在本例中为Enumerable)&#34;。在ConfigureMenu中,方法实现应定义为public final List<? extends Enumerable> getSubMenuOptions

有关使用泛型的更多有用详细信息是available here

答案 1 :(得分:0)

List<MenuOption>不是List<Enumerable>的类型,因为您无法向其中添加任何Enumerable。您可以改为返回List<? extends Enumerable>

public final List<? extends Enumerable> getSubMenuOptions
   (final ConfigureSubMenu subMenu) {

    switch(subMenu) {

        case SIMPLE_SUB_MENU:
            List<MenuOption> options = new SimpleSubMenu().getOptions();
            return options;
    }
}