Enum types in Java should implement some common interface
Some sort of interface a la Enum<T>, call it ConjureEnum<T>. It doesn't even necessarily need the same methods (except for a values method as in #78, and the valueOf method [perhaps with the guarantee that ConjureEnum#valueOf applied to ConjureEnum#toString always gives you back the same value])
This is useful in a few cases where I want to write some utility code that applies generally to enums, but after replacing with Conjure enums, I have to either abandon that or pass two classes (the internal enum value class as well as the outer class).
Examples:
Getting string values for printing in a drop-down menu:
public static <T extends Enum<T>> String[] valueNamesWithoutUnknown(Class<T> underlyingConjureEnumType) {
List<String> ret = Lists.newArrayList();
for (T val : underlyingConjureEnumType.getEnumConstants()) {
if (!val.name().equals("UNKNOWN")) {
ret.add(val.name());
}
}
return ret.toArray(new String[0]);
}
We frequently have enum classes with util methods that round-trip the values through an id (e.g. for serializing to a DB), so we unit test that the round-trip is stable:
public class PeeringConjureEnumUtilBaseTest<T, U extends Enum<U>> extends BaseTest {
// <snip>
protected final void testIdRoundTrips(Function<T, Long> getIdFunc,
Function<Long, T> fromIdFunc,
Class<U> clazz,
Function<U, T> fromEnumFunc) {
try (AutoCloseableSoftAssertions softAssert = new AutoCloseableSoftAssertions()) {
for (U enumValue : clazz.getEnumConstants()) {
if (!enumValue.name().equals("UNKNOWN")) {
T value = fromEnumFunc.apply(enumValue);
try {
T roundTrippedValue = fromIdFunc.apply(getIdFunc.apply(value));
softAssert.assertThat(roundTrippedValue)
.as("Round-tripping through converters should provide same value.")
.isEqualTo(value);
} catch (Throwable e) {
softAssert.assertThat(true)
.as("Failed to roundtrip value %s with exception %s",
value,
e.getMessage())
.isFalse();
}
}
}
}
}
In a property/configuration class where I know the values will take on the values of an enum and I want to validate that as well as return the right value:
@Override
public <T extends Enum<T>, U> U getEnum(PeerPropertyLookup remoteSystemId,
NPPeerProperty prop,
Class<T> enumClass,
Class<U> returnClass) {
Validate.isTrue(prop.getConjureEnumReturnType().equals(returnClass));
// This validates that it's a valid entry in the enum.
T enumReturn = getEnum(remoteSystemId, prop, enumClass);
// This is a tricksy way to call #valueOf that doesn't rely on reflection but does rely on the fairly safe
// assumption that Conjure will continue to make their enums Jackson-serializable in the future.
try {
return new ObjectMapper().readValue("\"" + enumReturn.name() + "\"", returnClass);
} catch (IOException e) {
throw new RuntimeException("Failed to parse enum for peer property " + prop + " into class " + returnClass.getName(),
e);
}
}
I think all of these would be significantly more simple and readable if I could rely on a <T extends ConjureEnum<T>> and I don't see a downside to doing so.