Skip to content

Get a compile time error when you forget to handle an enum constant (and do reverse enum-lookup)

License

Notifications You must be signed in to change notification settings

tmtron/enum-mapper

Repository files navigation

Build Status license

enum-mapper

The main use: make sure that you always handle all available constants of an enum. An annotation processor will make sure that you get a compile-time error otherwise: see Full Enum Mapper.
You can also use a Partial Mapper and it supports Reverse Mapping.
Since version 1.4 Incremental Annotation Processing is supported for Gradle builds.

Build Configuration

The project is available in Maven Central and JCenter-Bintray repositories.

    final VERSION_ENUM_MAPPER = '1.0.4' // check for newer versions here: https://goo.gl/LSP1fv
    implementation  "com.tmtron.enums:enum-mapper-lib:${VERSION_ENUM_MAPPER}"
    annotationProcessor "com.tmtron.enums:enum-mapper-processor:${VERSION_ENUM_MAPPER}"

enum-mapper-lib

Contains java code and annotations.
This is always needed at compile-time.
Maven Central lib Javadocs

enum-mapper-processor

Contains the annotation-processor.
This is needed by the annotation-processing build-step (apt) and it is only required for the Full Enum Mapper.
Maven Central processor Javadocs

Full Enum Mapper

Just annotate your enum with the @EnumMapper annotation:

@EnumMapper
public enum Seasons {
  SPRING, SUMMER, FALL, WINTER
}

When you build your project, the annotation processor will generate a java class Seasons_MapperFull, which can be used to map all enum constants to arbitrary values.

Here is an example use where we map each enum constant to a string.

EnumMapperFull<Seasons, String> germanSeasons = Seasons_MapperFull
     .setSPRING("Fruehling")
     .setSUMMER("Sommer")
     .setFALL("Herbst")
     .setWINTER("Winter");

String germanSummer = germanSeasons.getValue(Seasons.SUMMER); // returns "Sommer"

The great thing about this is that you will get a compile time error, when you forget to map any enum-constant - or when you change the enumeration (e.g. add or remove an enum-constant).

Detailed usage examples are also available on the github enum-mapper-example project

Third party enums

You can also generate full-mappers for Enums that you don't control. Just add the @EnumMappers (note the plural form!) annotation to any class and reference the enum classes that you want to map.

For example let's assume, that you use a 3rd party library which defines these enums:

public enum ColorEnum {
    RED, BLUE, GREEN
}
public enum BoolEnum {
    ON, OFF
}

Since you cannot change the source code of the 3rd party library, you cannot add the @EnumMapper annotation to the enum classes.
Instead you can use the the @EnumMappers (note the plural form!) annotation on any of your own classes, like this:

@EnumMappers({ColorEnum.class, BoolEnum.class})
public class AppUtil {
    // your code here
}

Then the annotation processor will create a full enum-mapper for ColorEnum and for BoolEnum.

Hint: when you want to map a single enum, you don't need the curly braces (for the array-syntax)

@EnumMappers(ColorEnum.class)
public class AppUtil {
    // your code here
}

Partial Enum Mapper

The project also includes a partial enum-mapper class which you may want to use instead of a switch statement.
Note. The partial enum mapper does not need the annotation processor.

An example of a partial mapper for your Seasons enum:

    EnumMapperPartial<Seasons, String> extremeSeasons =
            EnumMapperPartial.of(SUMMER, "Sommer"
                    , WINTER, "Winter");

Now you can call the getValueOrNull or getValueOrDefault methods like this:

extremeSeasons.getValueOrNull(SUMMER);                      // returns "Sommer"
extremeSeasons.getValueOrNull(WINTER);                      // returns "Winter"
extremeSeasons.getValueOrNull(SPRING));                     // returns null
extremeSeasons.getValueOrDefault(SPRING, "not extreme");    // returns "not extreme"
extremeSeasons.getValueOrRaise(SPRING);                     // throws an IllegalArgumentException
extremeSeasons.isEnumMapped(SUMMER);                        // returns true
extremeSeasons.isEnumMapped(SPRING);                        // returns false

Reverse mapping

The full and the partial mappers also support reverse lookup.

For example, we can use the extremeSeasons mapper to get the enum-constant for a string, like this:

extremeSeasons.getEnumOrNull("Sommer");                 // returns the enum-constant SUMMER
extremeSeasons.getEnumOrDefault("Fruehling", FALL));    // returns the enum-constant FALL
extremeSeasons.getEnumOrRaise("Fruehling");             // throws an IllegalArgumentException 
extremeSeasons.isValueMapped("Sommer");                 // returns true
extremeSeasons.isValueMapped("Fruehling");              // returns false

When you do a reverse mapping the mapped values should of course be unique.

Alternatives

This section mentions some alternative approaches that you can use instead of this annotation processor.
See also: Stackoverflow: How to ensure completeness in an enum switch at compile time?

Abstract Enum Methods

As mentioned in this Stackoverflow answer you can have abstract methods on your enum definition.

public enum AlternativeBool {

    ON {
        @Override
        public String getGermanName() {
            return "ein";
        }
    }
    , OFF {
        @Override
        public String getGermanName() {
            return "aus";
        }
    };


    public abstract String getGermanName();
}

Advantages

  • this approach does not need an annotation processor
  • you also get a compile-time error should you forget to implement a method for a new enum

Disadvantages

  • it is quite verbose
  • you can only use this for enums that you control

IDE checks

Some IDEs allow you to activate a check that will warn you when you forget an enum constant in a switch statement:

Advantages

  • this approach does not need an annotation processor
  • direct and immediate feedback

Disadvantages

  • you could forget to active the switch
  • someone members of your team may use other IDEs which don't support this feature

Other tools

For example FindBugs has a check Switch statement found where default case is missing SF_SWITCH_NO_DEFAULT

Advantages

  • other tools may offer way more functionality

Disadvantages

  • those are other tools that you must learn to use and maintain

License

This plugin is under the Apache 2.0 license. Copyright 2018, Martin Trummer

About

Get a compile time error when you forget to handle an enum constant (and do reverse enum-lookup)

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages