Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

java

enums

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?

Moon something = <??planetName??>.valueOf(moonName);
like image 666
matandked Avatar asked Mar 04 '23 19:03

matandked


2 Answers

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:

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

The above will return 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). 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.

like image 70
ernest_k Avatar answered Apr 27 '23 22:04

ernest_k


If you really want it to be dynamic, you could achieve this using Reflection, event if I don't recommend it. You could either

  • Get all the classes that implement Moon, iterate on them and check if they have an instance called PHOBOS.
  • Get the class Mars by its name dynamically and look for the instance. One drawback is that if you move the class Mars to another package, it will break.
  • Get all the classes of your package, keep the one named Mars which implements Moon and look for the instance. This is kind of overkill.
like image 23
Ricola Avatar answered Apr 27 '23 22:04

Ricola