Home > database >  Nested Enum constructor
Nested Enum constructor

Time:04-05

Not sure if I'm over engineering or what, but I was thinking of creating an enum that would have a list of enums as it's value, from which I could get the value of that.

enum Country {
  US(UnitedStates.values()),
  CA(Canada.values());

  public ? list;

  Country(? list) { this.list = list; }
}
enum UnitedStates {
  NJ(new NJClass()),
  PA(new PAClass());

  private BaseKlass klass;
  UnitedStates(BaseClass name){ this.klass = name }
  public BaseKlass getKass() { return klass; }
}
enum Canada {
  ON(new ONClass());

  private BaseKlass klass;
  Canada(BaseKlass name){ this.klass = name }

  public BaseKlass getKass() { return klass; }
}

I'm having trouble figuring the type for the Country enum in order to properly filter that values array to the correct enum. For example, I can get the US enum easily with Country.valueOf("US"). Where I'm having trouble is getting the correct value out of that array. I have tried comparing the name, Country.valueOf("US").stream().filter(e -> e.name().equals("NJ")).findFirst().get().getKlass() But it's never the correct type.

Is this even possible or am I over thinking things?

CodePudding user response:

definitely overengineering ... I would have gone with

enum Country {
  US(<array/list of us states is enough>),
  CA(<array/list of ca states is enough>);

  public List<State> list;

  Country(List<State> list) { this.list = list; }
}

CodePudding user response:

Considering that you can't make Enum extend some custom parent class in order to make the abstraction that you require to, I would follow the following approach

public enum States {

    ALABAMA("Alabama", Country.US),
    ARIZONA("Arizona", Country.US),
    ON("Ontario", Country.CA),
    QC("Quebec", Country.CA),
    NS("Nova Scotia", Country.CA);

    States(String name, Country country){
        this.name = name;
        this.country = country;
    }

    private String name;
    private Country country;

    public List<States> getByCountry(Country country){
        return Arrays.stream(States.values()).filter(s -> s.country.equals(country)).collect(Collectors.toList());
    }
}

You can also keep those States in memory during initialization so that you don't compute every time which states belong to which country.

public enum States {

    ALABAMA("Alabama", Country.US),
    ARIZONA("Arizona", Country.US),
    ON("Ontario", Country.CA),
    QC("Quebec", Country.CA),
    NS("Nova Scotia", Country.CA);

    States(String name, Country country){
        this.name = name;
        this.country = country;
    }

    private String name;
    private Country country;

    private static Map<Country, List<States>> statesMap = initializeStatesMap();
    private static Map<Country, List<States>> initializeStatesMap(){
        return Arrays.stream(States.values())
                .collect(Collectors.groupingBy(state -> state.country,
                        Collectors.mapping(state -> state, Collectors.toList())));
    }

    public static List<States> getByCountry(Country country){
        return statesMap.containsKey(country) ? statesMap.get(country) : new ArrayList<>();
    }
}

and then

public enum Country {

    US,
    CA

}

CodePudding user response:

It is possible to define interface every state of a country must implement. Then you can just initialize every country enum constant with enum class of particular state. Thanks to generic bounds, the country enum constructor has enough information to build list of states:

    class BaseKlass {}
    class USState extends BaseKlass {}
    class CAState extends BaseKlass {}
    class NJClass extends USState {}
    class PAClass extends USState {}
    class ONClass extends CAState {}
    interface BaseSubclassAware<S extends BaseKlass> {
        S getKlass();
    }
    
    enum UnitedStates implements BaseSubclassAware<USState> {
        NJ(new NJClass()),
        PA(new PAClass());

        private USState klass;
        UnitedStates(USState name){ this.klass = name; }
        public USState getKlass() { return klass; }
    }
    enum Canada implements BaseSubclassAware<CAState> {
        ON(new ONClass());

        private CAState klass;
        Canada(CAState name){ this.klass = name; }

        public CAState getKlass() { return klass; }
    }
    enum Country {
        US(UnitedStates.class),
        CA(Canada.class);

        public List<? extends BaseKlass> list;

        <S extends BaseKlass, E extends Enum<E> & BaseSubclassAware<S>> Country(Class<E> clazz) {
            this.list = Stream.of(clazz.getEnumConstants()).map(BaseSubclassAware::getKlass).collect(Collectors.toList()); 
        }
        
    }

This is just a demo to literally fullfill your requirement and demonstrate some points where generics can be useful. From practical point of view it is definitely overengineering - it is usage of both enum constants and classes in a weird mix.

  • Related