Home > Mobile >  Java Generics with Enum; return type?
Java Generics with Enum; return type?

Time:04-30

I wrote this function in my Java program:

public <T extends Enum<T>> T getEnum(Class<T> enumClass, String name) {
    String value = get(name);
    if (value == null) return null;
    if (value.length() == 0) return null;
    return Enum.valueOf(enumClass, value);
}

This is how I'm calling it:

CollegeDegreeType degreeType = model.getEnum(CollegeDegreeType.class, "degree_type");

It works, but I feel like I should be able to omit the first parameter and infer it from the return type (the type to which the result is being assigned). Is this possible? If so, what's the syntax?

CodePudding user response:

It works, but I feel like I should be able to omit the first parameter and infer it from the return type.

You will not be able to provide T at runtime unless you have either of these:

  • method argument of type T (which you want to omit);
  • or an instance T comes as a result of the method call;
  • a hard-coded instance of enum type (which is frankly pointless).

Example (the second and the third option):

public static <T extends Enum<T>> T getEnum(String name) {
    T myEnum = (T) getHardCodedEnumMember();
    
    return (T) Enum.valueOf(myEnum.getClass(), name);
}

public static <T extends Enum<T>> T getHardCodedEnumMember() {
    return (T) DayOfWeek.SATURDAY;
}

That will work, but I guess that isn't your intention, so I have to stick with passing the Class instance as a parameter.

Another thing to consider is that the method that you've listed might have three outcomes:

  • it can return an enum element;
  • it might return null value;
  • or throw an exception.

Method valueOf() will throw IllegalArgumentException in case if there's no enum member with the matching name. And prior check against null and empty string will not save you from it. At the same time, you need to deal with a nullable result.

You can take another approach by elimination the runtime exceptions and reducing possible outcomes to either an empty optional or optional containing a result. For that you might make use of EnumSet.allOf() to retrieve all contants of the spesified enum type.

public <T extends Enum<T>> Optional<T> getEnum(Class<T> enumClass, String name) {
    String value = get(name);
    if (value == null || value.length() == 0) {
        return Optional.empty();
    }
    
    return EnumSet.allOf(enumClass).stream()
            .filter(element -> element.name().equals(value))
            .findFirst();
}

CodePudding user response:

Due to generics type erasure the type T is only known at compile time, this prevents you from accessing its class at runtime which you need for Enum.valueOf.

Your current solution for getting the class is a fine approach in my opinion.

See https://docs.oracle.com/javase/tutorial/java/generics/erasure.html

  • Related