Problem
We have multiple enum types that have some public static EnumType valueOfText(String text), for the purpose of mapping the contents of a data file cell to enum.
I'm trying to write a generic enum util that takes a comma-separated string and return multiple enum values. For example, we have the following enum:
public enum Frequency {
SEMI_ANNUAL("S"), MONTHLY("M"), QUARTERLY("Q"), ANNUAL("A")
public final String textValue;
public Frequency(String textValue) {
this.textValue = textValue;
}
public static Frequency valueOfText(String textValue) {
for (Frequency frequency : values()) {
if (frequency.textValue.equals(textValue))
return frequency;
}
return null;
}
}
and string "A,S" which we want to convert to [Frequency.ANNUAL, Frequency.SEMI_ANNUAL].
Attempted solution
I create some EnumUtils like so:
import java.util.stream.Collectors
public final class EnumUtils {
public static final String LIST_SEPARATOR = ",";
public static <E extends Enum<E>> List<E> CreateFromText(String text) {
List<String> textList = text.split(this.LIST_SEPARATOR)
return textList.stream()
.map { txt ->
E.valueOfText(txt)
}
.collect(Collectors.toList())
}
}
What happen after said solution
We go to use it, like this:
EnumUtils.CreateFromText<Frequency>(row[3])
and the IDE compain, immediately, about the <>.
How can we specify enum type in this?
In Groovy you can do it if you pass the actual Class instead of just using a type parameter.
enum Frequency {
SEMI_ANNUAL("S"), MONTHLY("M"), QUARTERLY("Q"), ANNUAL("A")
final String textValue;
Frequency(String textValue) {
this.textValue = textValue;
}
static Frequency valueOfText(String textValue) {
return values().find { it.textValue == textValue }
}
}
final class EnumUtils {
static <E extends Enum<E>> List<E> createFromText(Class<E> clazz, String text) {
return text.split(",").collect { clazz.valueOfText(it) }
}
}
EnumUtils.createFromText(Frequency, "S,M")
The same idea won't work in Java, since clazz won't have valueOfText at compile time.
Perhaps the Util class doesn't save you much typing, though:
"S,M".split(",").collect(Frequency.&valueOfText)
Related
This is an odd question. I don't think there's a solution, but I thought I'd ask anyway.
Say I have an enum:
public enum Key {
RED(String.class),
GREEN(Integer.class),
BLUE(Short.class);
private Class<?> expectedType;
Key(Class<?> expectedType) { this.expectedType = expectedType; }
public Class<?> getExpectedType() { return expectedType; }
}
I want to use the 'expectedType' field from the Key enum as the return type of a method. See:
public class Cache {
private static Map<Key, Object> cache = new HashMap<>();
public void put(Key key, Object value) {
// Easy to validate that 'value' is of type key.getExpectedType()...
}
public <T> T get(Key key) {
Object value = cache.get(key);
// TODO need to define <T> as key.getExpectedType(). How?
}
}
See that TODO? I'd like for get() to define the return type of the 'expectedType' defined by the key parameter. E.g. if the key parameter were RED, the get() method would return a String and you could write:
String s = cache.get(Key.RED);
Is there a way to do that?
I'm thinking there isn't, but I'd love to hear of a clever solution.
Enums don't support generics, but you could use a regular class as a generic pseudo-enum:
public class Key<T> {
public static final Key<String> RED = new Key<>(String.class);
public static final Key<Integer> GREEN = new Key<>(Integer.class);
public static final Key<Short> BLUE = new Key<>(Short.class);
private final Class<T> expectedType;
private Key(Class<T> expectedType) { this.expectedType = expectedType; }
public Class<T> getExpectedType() { return expectedType; }
}
public class Cache {
private Map<Key<?>, Object> cache = new HashMap<>();
public <T> void put(Key<T> key, T value) {
cache.put(key, key.getExpectedType().cast(value));
}
public <T> T get(Key<T> key) {
return key.getExpectedType().cast(cache.get(key));
}
}
shmosel's answer is almost certainly sufficient for what you need; however, it has the slight limitation that you can't store/retrieve a generic type, because you can't get a class literal for a generic type.
Instead, you can use something like Guava's TypeCapture:
abstract class GenericKey<T> {
Type getExpectedType() {
return ((ParameterizedType) getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
}
}
which is a bit of reflective grossness that you shouldn't spend too much time looking at.
Notice that it's abstract, so you have to instantiate like:
new GenericKey<Integer>() {}
This is creating an anonymous subclass of GenericKey, which is part of the magic that makes it work with generic types.
Then, it's basically the same:
public class Cache {
private Map<GenericKey<?>, Object> cache = new HashMap<>();
public <T> void put(GenericKey<T> key, T value) {
cache.put(key.getExpectedType(), value);
}
public <T> T get(GenericKey<T> key) {
return (T) cache.get(key.getExpectedType());
}
}
Now you could have a GenericKey<List<Integer>>, using new new GenericKey<List<Integer>() {}, if you should so desire.
The downside of this approach is that you lose the ability to do checking on the value on the way in/out of the cache, so you could get heap pollution if you are careless with raw types.
I have Enumutils like below:
public interface EnumUtil {
String getValue();
static <T extends Enum<T> & EnumUtil> T fromValue(String enumValue, Class<T> type) {
EnumSet<T> all=EnumSet.allOf(type);
for (final T t: all) {
System.out.println("Value: " + t.getValue());
System.out.println("Name: " + t.name());
T val = T.valueOf(t.getDeclaringClass(), t.name()).;
System.out.println("ValValVal: " + val);
if (t.getValue().equalsIgnoreCase(enumValue)) {
return t;
}
}
return null;
}
}
Then I have created an enum which appears as follows:
#Getter
#AllArgsConstructor
public enum SupportedOptions implements EnumUtil {
PART("PART"),
MSRV("MSRV");
private final String value; //If we add this line then we need not to override getValue()
public static SupportedOptions fromValue(final String text) {
return EnumUtil.fromValue(text, SupportedOptions.class);
}
}
This works fine without any compilation issues. (getValue also returns the enum parameter successfully in fromvalue in EnumUtils interface for this enum)
But in the following scenario it results in a compile time exception (need to implement abstract methods).
#Getter
#AllArgsConstructor
public enum RejectedResponseCode implements EnumUtil {
UNPR("300", "849", "700", "701", "702", "703", "705", "730","704"),
IMSG("302", "105", "113", "114"),
PARS("107", "100", "102", "103", "115","720"),
SECU("302", "668", "669", "670", "671");
//approach 1
private final String value; //Here it want some value as i am using constructor like below
RejectedResponseCode(final String... codes) {
this.codes = Arrays.asList(codes);
//this.value = getValue(); <--- Is this approach fine. (For appraoch 1)
}
private final List<String> codes;
public static RejectedResponseCode getValueOfData(final String value) {
final Optional<RejectedResponseCode> result = Arrays.stream(values()).filter(rejectedResponseCode -> rejectedResponseCode.codes.contains(value)).findFirst();
if (result.isPresent()) {
return result.get();
}
return null;
}
public static RejectedResponseCode fromValue(final String text) {
return EnumUtil.fromValue(text, RejectedResponseCode.class);
}
//Approach 2
//#Override
//public String getValue(){
// return null; //Is this approach correct?
//}
}
How to ignore either not to override getValue()?
Or how to override getValue() in such a way that it returns parameter
or any other way to manage this in EnumUtil?
You need to search each enum value supporting multiple search keys, so implementing getValue is no use. On way around this is to reverse the operation such that the enum supplies a set of search criteria to be used, and implement a simple utility method on EnumUtil:
public class EnumUtil {
#SafeVarargs
public static <T,V> T match(T[] values, V fieldValue, BiPredicate<T,V> ... checks) {
for (var pred : checks)
for (T item : values)
if(pred.test(item, fieldValue))
return item;
return null; // OR throw new IllegalArgumentException("Not found: "+fieldValue);
}
}
Then your enum classes can setup any number of search parameters. SupportedOptions just matches on name:
enum SupportedOptions {
PART,
MSRV;
public static SupportedOptions fromValue(final String text) {
return EnumUtil.match(values(), text.toUpperCase(), (e, s) -> e.name().equals(s));
}
}
RejectedResponseCode matches on name() and codes.indexOf:
enum RejectedResponseCode {
UNPR("300", "849", "700", "701", "702", "703", "705", "730","704"),
IMSG("302", "105", "113", "114"),
PARS("107", "100", "102", "103", "115","720"),
SECU("302", "668", "669", "670", "671");
RejectedResponseCode(final String... codes) {
this.codes = Arrays.asList(codes);
}
private final List<String> codes;
public static RejectedResponseCode fromValue(final String text) {
return EnumUtil.match(values(), text.toUpperCase(), (e, s) -> e.name().equals(s), (e, s) -> e.codes.indexOf(s) >= 0);
}
}
So, anything can be located by name:
public static void main(String[] args)
{
for (String s : new String[] { "PART", "msrv", "other"})
System.out.println("SupportedOptions.fromValue("+s+") => "+ SupportedOptions.fromValue(s));
for (String s : new String[] { "UNPR", "imsg", "102", "671", "999999"})
System.out.println("RejectedResponseCode.fromValue("+s+") => "+ RejectedResponseCode.fromValue(s));
}
SupportedOptions.fromValue(PART) => PART
SupportedOptions.fromValue(msrv) => MSRV
SupportedOptions.fromValue(other) => null
RejectedResponseCode.fromValue(UNPR) => UNPR
RejectedResponseCode.fromValue(imsg) => IMSG
RejectedResponseCode.fromValue(102) => PARS
RejectedResponseCode.fromValue(671) => SECU
RejectedResponseCode.fromValue(999999) => null
As directed by Joachim Sauer in comment:
EnumUtil.fromValue() simply assumes that there's always exactly one identifiying string for each enum value. That's not true for your second sample. So either don't use EnumUtil.fromValue() or extend it to also support multiple values (probably by having a second interface that can return an array or collection of potentital identifiers) –
Accordingly, EnumUtils Removed from enum and created methods inside enums only.
I have an Enum marked with #JsonProperty for JSON serialization/deserialization with Jackson and would like to get the enum value for a given String JsonProperty:
public enum TimeBucket {
#JsonProperty("Daily") DAY_BUCKET,
#JsonProperty("Weekly") WEEK_BUCKET,
#JsonProperty("Monthly") MONTH_BUCKET;
}
The desired method should be generic/static (so it would not be necessary to replicate it in each of the enums) and would extract an enum value out of one of the JsonProperties:
public static <T extends Enum<T>> T getEnumFromJsonProperty(Class<T> enumClass, String jsonPropertyValue)
The desired result can be achieved through the following method:
public static <T extends Enum<T>> T getEnumValueFromJsonProperty(Class<T> enumClass, String jsonPropertyValue) {
Field[] fields = enumClass.getFields();
for (int i=0; i<fields.length; i++) {
if (fields[i].getAnnotation(JsonProperty.class).value().equals(jsonPropertyValue)) {
return Enum.valueOf(enumClass, fields[i].getName());
}
}
return null;
}
I created two Java enums,
public enum TypeEnum {
TYPE_A, TYPE_B
}
and
public enum FormatEnum{
FORMAT_X, FORMAT_Y
}
Next, I wrote two functions to convert an incoming String to an enum value:
private TypeEnum convertType(String test) {
return TypeEnum.valueOf(test);
}
private FormatEnum convertFormat(String test) {
return FormatEnum.valueOf(test);
}
Next, I wanted to unify these two conversion methods under a single method with generics. I tried this in two ways:
private <T extends Enum> Enum convertToEnumValue(T localEnum, String value) {
return T.valueOf(localEnum.getClass(), value);
}
and
private static <T extends Enum> T convertToEnumValue(Class<T> enumType, String value) {
return (T) T.valueOf(enumType, value);
}
I couldn't write a call to these methods that would compile.
Is there a way to correct them to make them work?
There is no need to declare your own method, as JDK java.lang.Enum already declares one:
FormatEnum y =Enum.valueOf(FormatEnum.class, "FORMAT_Y");
TypeEnum a = Enum.valueOf(TypeEnum.class, "TYPE_A");
This works because Enum is the base class of all enum types and so when you call TypeEnum.valueOf(s); you are calling Enum.valueOf(s)
…Is there a way to correct them to make them work?…
I got your examples to work with these very small corrections…:
class DeduperAnswer {
private <T extends Enum> T convertToEnumValue(T localEnum, String value) {
return ( T ) T.valueOf(localEnum.getClass(), value);
}
private static <T extends Enum> T convertToEnumValue(Class<T> enumType, String value) {
return ( T ) T.valueOf(enumType, value);
}
static public void main(String ...args){
DeduperAnswer da = new DeduperAnswer();
TypeEnum typB = da.convertToEnumValue(TypeEnum.TYPE_B, "TYPE_B");
FormatEnum fmtX = convertToEnumValue(FormatEnum.FORMAT_X.getClass(), "FORMAT_X");
}
}
Of course, there's more than one way to skin a cat — as the saying goes. But seeing as your solution works for you, you're good to go.
I suspect you are looking for the following method:
public static <E extends Enum<E>> E toMember(Class<E> clazz, String name) {
//TODO input validations;
for (E member : clazz.getEnumConstants()) {
if (member.name().equals(name)) {
return member;
}
}
return null; //Or throw element not found exception
}
//More elegant form of the previous one
public static <E extends Enum<E>> E toMember(Class<E> clazz, String name, E defaultMember) {
//TODO input validations;
for (E member : clazz.getEnumConstants()) {
if (member.name().equals(name)) {
return member;
}
}
return defaultMember;
}
Note the generic E extends Enum<E>
I have a class for example
public class Example<T> {...}
I would like to instantiate class Example with a specific type class which I know. Pseudocode would look something like that
public Example<T> createTypedExample(Class exampleClass, Class typeClass) {
exampleClass.newInstance(typeClass); // made-up
}
So that this would give me same result
Example<String> ex = new Example<String>();
ex = createTypedExample(Example.class, String.class);
Is it possible in Java?
Since, the return type i.e. the class of the new instance is fixed; there's no need to pass it to the method. Instead, add a static factory method to your Example class as
public class Example<T> {
private T data;
static <T> Example<T> newTypedExample(Class<T> type) {
return new Example<T>();
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
Now, here's how you would create generic Example instances.
// String
Example<String> strTypedExample = Example.newTypedExample(String.class);
strTypedExample.setData("String Data");
System.out.println(strTypedExample.getData()); // String Data
// Integer
Example<Integer> intTypedExample = Example.newTypedExample(Integer.class);
intTypedExample.setData(123);
System.out.println(intTypedExample.getData()); // 123