Home > Back-end >  Refactoring - generating a List of Strings from an Enum
Refactoring - generating a List of Strings from an Enum

Time:03-22

Method getContinent() expects a String as an argument which can have three possible values: "Africa", "Asia" and "Europe".

Depending on the given value I get, I have to match the appropriate enum and get a list of all its values (as String).

There are repetitions in my code. Is there a better way to do it? Is it a good idea to place this in each of the enum's?:

Stream.of(CountryAsia.values())
      .map(country -> country.getName())
      .collect(Collectors.toList());

My code

Main class:

public class Main {
    public static void main(String[] args) {
        String continent = getContinent() //one of three possible values: "Africa" or "Asia" or "Europe"
        List<String> list = null;

        switch(continent) {
            case "Africa":
                list = Stream.of(CountryAfrica.values())
                        .map(country -> country.getName())
                        .collect(Collectors.toList());
                break;
            case "Asia":
                list = Stream.of(CountryAsia.values())
                        .map(country -> country.getName())
                        .collect(Collectors.toList());
                break;
            case "Europe":
                list = Stream.of(CountryEurope.values())
                        .map(country -> country.getName())
                        .collect(Collectors.toList());
                break;
        }

        System.out.println(list);
        //next instructions..
    }
}

CountryEurope enum:

public enum CountryEurope {
    DENMARK("Denmark"),
    ESTONIA("Estonia"),
    ICELAND("Iceland");

    CountryEurope(String name) {
        this.name = name;
    }

    private final String name;

    public String getName() {
        return name;
    }
}

CountryAsia enum:

public enum CountryAsia {
    AFGHANISTAN("Afghanistan"),
    KAZAKHSTAN("Kazakhstan"),
    UNITED_ARAB_EMIRATES("United Arab Emirates"),
    UZBEKISTAN("Uzbekistan");

    CountryAsia(String name) {
        this.name = name;
    }

    private final String name;

    public String getName() {
        return name;
    }
}

CountryAfrica class:

public enum CountryAfrica {
    ALGIERIA("Algeria"),
    WESTERN_SAHARA("Western Sahara"),
    EGYPT("Egypt");

    CountryAfrica(String name) {
        this.name = name;
    }

    private final String name;

    public String getName() {
        return name;
    }
}

CodePudding user response:

You can make use EnumSet.allOf() in order to get the names of your enum constants.

EnumSet.allOf() expects a Class<T> of enum as an argument and returns an EnumSet of its elements (which is special implementation of the Set designed exclusivelly for enums).

Then we can transform a Set of enum constants into a list of strings either by using a stream or a plain for loop.

And if you are using Java 14 , you cane utilize switch expressions. Otherwise, replace the repeating stream statement with a method call.

In order to access the method getName() with every enum we can define an interface which all of them will implement.

public interface CountryData {
    String getName();
}

Also, it could contain some other methods related to the domain-specific information that could be useful in your application like country area or locale tax rates.

Combining all mentioned above will give a very concise and expressive code:

public static List<String> getCountries(String continent) {
    return switch(continent) {
        case "Africa" -> getEnumNames(CountryAfrica.class);
        case "Asia" -> getEnumNames(CountryAsia.class);
        case "Europe" -> getEnumNames(CountryEurope.class);
        default -> Collections.emptyList();
    };
}

public static <T extends Enum<T> & CountryData> List<String> getEnumNames(Class<T> enumClas) {
    return EnumSet.allOf(enumClas).stream()
            .map(CountryData::getName)
            .collect(Collectors.toList());
}

main()

public static void main(String[] args) {
    System.out.println(getCountries("Europe"));
    System.out.println(getCountries("Africa"));
    System.out.println(getCountries("Asia"));
}

Output

[Denmark, Estonia, Iceland]
[Algeria, Western Sahara, Egypt]
[Afghanistan, Kazakhstan, United Arab Emirates, Uzbekistan]

Is it a good idea to place this in each of the enum's?

No, it's not because:

  • the code it will not eliminate the duplication, the same lines will be scattered across multiple files;
  • this code ins't specific to any of your enums therefore it must reside in one place (in a class that is meant to process this list).

CodePudding user response:

Looks like you try to store some collection of data inside enum that is a bit wrong IMHO. I would prefer to provide a map where the key is "continent" and the value is a collection of countries. Map<String, Set>.

Then, you don't need to use the switch, you can use Map#get().

  • Related