简体   繁体   English

如何获得在Java中实现特定接口的正确枚举?

[英]How can I get the proper enum that implements a particular interface in Java?

I have the following interface: 我有以下界面:

public interface Moon {
    // Enums can not extend any class in Java,
    // thus I am implementing an interface
    double getMass();
    double getDiameter();
}

that is implemented by my enums (I omitted the constructor and methods implementation) 这是由我的枚举实现的(我省略了构造函数和方法实现)

public enum Saturn implements Moon {
    ATLAS(30.2, 6.6),
    PAN(28.2, 4.95);

    private double Diameter;
    private double Mass;

    // constructor and methods implementation
}

and similarly enums for other planets: 和其他行星类似的枚举:

public enum Mars implements Moon {
    PHOBOS(22.2, 1.08),
    DEIMOS(12.6, 2);

    private double Diameter;
    private double Mass;

    // constructor and methods implementation
}

Question

Having two strings: 有两个字符串:

String planetName = "Mars";
String moonName = "PHOBOS";

How can I obtain the particular enum in a smart way? 我怎样才能以聪明的方式获得特定的枚举?

In the case of only one enum class it is a simple use of the valueOf method, but how can I check all enums that implement a particular interface? 在只有一个枚举类的情况下,它是valueOf方法的简单使用,但是如何检查实现特定接口的所有枚举?

Moon something = <??planetName??>.valueOf(moonName);

You can use a mapping of planet names to related enum types, and use that to perform a lookup. 您可以使用行星名称到相关枚举类型的映射,并使用它来执行查找。

For example, using a map (there are surely alternative approaches): 例如,使用地图(肯定有替代方法):

Map<String, Class<? extends Enum>> planetMoonTypes = new HashMap<>();

planetMoonTypes.put("Saturn", Saturn.class);
planetMoonTypes.put("Mars", Mars.class);

With such a map, you can lookup the "PHOBOS" value using: 使用这样的映射,您可以使用以下命令查找"PHOBOS"值:

Moon phobos = (Moon) Enum.valueOf(planetMoonTypes.get("Mars"), "PHOBOS");

The above will return Mars.PHOBOS 以上将返回Mars.PHOBOS


EDIT: Regarding the ability to add non-Moon enums. 编辑:关于添加非月亮枚举的能力。 You can encapsulate the map and force proper bounds. 您可以封装地图并强制使用适当的边界。 For example, the following uses a method for that: 例如,以下使用方法:

class MoonRegistry {

    private final Map<String, Class<? extends Enum>> 
              planetMoonTypes = new HashMap<>();

    {
        this.registerEnum("Saturn", Saturn.class);
        this.registerEnum("Mars", Mars.class);
    }

    public <T extends Enum<T> & Moon> void registerEnum(
            String planetName, Class<T> cls) {
        this.planetMoonTypes.put(planetName, cls);
    }

    public Moon findByPlanetAndName(String planet, String name) {
        return (Moon) Enum.valueOf(this.planetMoonTypes.get(planet), name);
    }
}

Again, this solution cannot be fully type-safe because there are multiple enum subtypes (as you can see, the raw Enum type is in the type of the instance field). 同样,此解决方案不能完全类型安全,因为有多个枚举子类型(如您所见,原始Enum类型是实例字段的类型)。 But if this is properly encapsulated and your code uses no raw types outside this class, you can ensure that only enum types implementing Moon are added. 但是如果这是正确封装的并且您的代码在此类之外不使用原始类型,则可以确保仅添加实现Moon枚举类型。

If you really want it to be dynamic, you could achieve this using Reflection, event if I don't recommend it. 如果你真的希望它是动态的,你可以使用Reflection,事件,如果我不推荐它。 You could either 你可以

I suspect you have a weakness in your design that is causing you to look for complex ways to obtain the enum instance you need. 我怀疑你的设计有一个弱点,导致你寻找复杂的方法来获得你需要的枚举实例。 Having a separate enum for each planet probably doesn't make a lot of sense when they are all unique moons. 每个行星都有一个单独的枚举,当它们都是独特的卫星时,可能没有多大意义。 Better to rethink the design a bit. 最好重新考虑一下设计。 Something like: 就像是:

interface Body {
    double getMass();
    double getDiameter();
}

enum Planet implements Body {
    MERCURY, VENUS...
}

enum Moon implements Body {
    ATLAS, PHOBOS, DEIMOS...

    public Planet getPlanet();
}

That way it's easy to get planets or moons by name. 这样就很容易通过名字获得行星或卫星。 Or get all the moons of a planet with: 或者通过以下方式获取行星的所有卫星:

Arrays.stream(Moon.values())
    .filter(m -> m.getPlanet().equals(this))
    ...

If all your enums are in the same package, then this solution could work. 如果您的所有枚举都在同一个包中,那么此解决方案可以正常工作。

String planetName = "Mars";
String moonName = "PHOBOS";

try {
    Moon moons[] = (Moon[]) Class.forName("your_package_name" + planetName).getEnumConstants();

    // The following line is not really needed, but I've added it as an 
    // illustration as to what you could do with the moons[]
    Moon planet = Arrays.stream(moons).filter(i -> i.toString().equals(moonName))
                          .findFirst().orElse(null);

} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

Consider using an extra enum Plantes with its moons assigned as an enum property like this 考虑使用额外的enum Plantes ,将其卫星分配为这样的枚举属性

public enum Planets {
    MARS(Mars.values()), SATURN(Saturn.values());

    private final Enum<?>[] Moons;

    private Planets(final Enum<?>[] Moons) {
        this.Moons = Moons;
    }

    public Moon getMoon(final String moon) {
        return (Moon) Arrays
                .stream(this.Moons)
                .filter(e -> e.name().equals(moon))
                .findFirst()
                .get();
    }
}

It takes the Moons from the already defined enums and assigned it to an enum for each planet. 它从已定义的枚举中获取月亮并将其分配给每个行星的枚举。 There is also a getMoon() (function valueOf() is already used by enum itself), which searches for a moon. 还有一个getMoon() (函数valueOf()已被enum本身使用),它搜索月亮。

You can use it like this: 你可以像这样使用它:

    // Outputs 22.2
    System.out.println("" + Planets.valueOf("MARS").getMoon("PHOBOS").getDiameter());

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM