Get Enum constant by value - java

I want to create a method, which returns enum constants by it's value.
I have multiple enum classes which looks like this one (some of them have getter variables with different names):
#AllArgsConstructor
public enum PhoneType {
MOBILE("Mobile", 3),
HOME("Home", 6),
WORK("Work", 7);
#Getter
String type;
#Getter
int id;
}
I used this stream to get the enum constant:
int phoneTypeId = 3;
PhoneType phoneType = Arrays.stream(PhoneType.values())
.filter(p -> p.getId() == phoneTypeId)
.findFirst()
.orElseThrow(() -> new RuntimeException("Not able to find Enum...."));
System.out.println(phoneType.getType());
output is: "Mobile"
Now I want to create a method which will work for different enum classes. I started with something like this, but I don't know how to rewrite the filter line, to make it work for any enum class. It would be ideal to pass this "p -> p.getId() == phoneTypeId" as input parameter to this method. Any ideas?
public static <E extends Enum<?>> E getEnumByValue(Class<E> enumClass) {
return Arrays.stream(enumClass.getEnumConstants())
.filter(p -> p.getId() == phoneTypeId)
.findFirst()
.orElseThrow(() -> new RuntimeException("Not able to find Enum...."));
}
(I know that it's possible to do that if I implement interface for all enum classes, but then enum variables would have to have the same names.)

You can pass it as a Predicate:
public static <E extends Enum<?>> Optional<E> getEnumByValue(Class<E> enumClass, Predicate<E> predicate) {
return Arrays.stream(enumClass.getEnumConstants())
.filter(predicate)
.findFirst();
}
(Note: the method should actually return an Optional<E>)
If all enums have ids, you can still implement a common interface:
interface Identifiable {
int getId();
}
enum PhoneType implements Identifiable {
...
}
public static <E extends Enum<?> & Identifiable> Optional<E> getEnumById(Class<E> enumClass, int id) {
return Arrays.stream(enumClass.getEnumConstants())
.filter(e -> e.getId() == id)
.findFirst();
}
Optional<PhoneType> phone = getEnumById(PhoneType.class, phoneTypeId);

Related

Write a single generics method to cover multiple methods for String to Enum value conversion

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>

Function with variable parameter type

I have these two functions which are basically the same:
private Order getOrder( List<Order> orders, String gcsId )
{
Predicate<Order> predicate = c -> c.getGcsId().equals( gcsId );
Order obj = orders.stream().filter( predicate ).findFirst().get();
return obj;
}
private OrderLine getOrderLine( List<OrderLine> orderLines, String gcsId )
{
Predicate<OrderLine> predicate = c -> c.getGcsId().equals( gcsId );
OrderLine obj = orderLines.stream().filter( predicate ).findFirst().get();
return obj;
}
The only difference is the type of the first parameter. How can I create a single function with the first parameter having a variable type?
Edit: This is how I call those functions in another method of the same class:
Order order = getOrder( orders, orderId );
OrderLine orderLine = getOrderLine( order.getOrderLines(), orderLineId );
First you should create an interface like that and your classes should implement this interface:
public interface IOrder {
String getGcsId();
}
public class Order implements IOrder {
// Content of your class
}
public class OrderLine implements IOrder {
// Content of your class
}
After that you can write your method like that using generics:
private <T extends IOrder> T getOrder( List<T> orders, String gcsId )
{
Predicate<T> predicate = c -> c.getGcsId().equals( gcsId );
T obj = orders.stream().filter( predicate ).findFirst().get();
return obj;
}
Either make them implement a common interface, which defined the getGcsId() method, and use
private <T extends CommonInterface> T getFirstWithGcsId(List<T> orders, String gcsId) {
Predicate<T> predicate = c -> c.getGcsId().equals(gcsId);
T obj = orders.stream().filter(predicate).findFirst().get();
return obj;
}
Or change the signature of the method to pass it a function which knows how to extract the gcsId:
private <T> T getFirstWithGcsId(List<T> orders, String gcsId, Function<T, String> gcsIdExtractor) {
Predicate<T> predicate = c -> gcsIdExtractor.apply(c).equals(gcsId);
T obj = orders.stream().filter(predicate).findFirst().get();
return obj;
}
But this method is so simple that, frankly, if you don't have any common interface, I would just inline it. It's basically one line of code:
Order firstOrder = orders.stream().filter(o -> o.getGcsId().equals(gcsId)).findFirst().get();
You should also avoid calling get() on an Optional in general. Rather prefer something like
Order firstOrder = orders.stream().filter(o -> o.getGcsId().equals(gcsId)).findFirst().orElseThrow(() -> new IllegalStateException("no first order with gcsId " + gcsId));
If you give a List of Order you return an Ornder, if you give an define a generic method instead:
private <T extends Foo> T getOrder(List<T > orders, String gcsId) {
Predicate<T> predicate = c -> c.getGcsId().equals(gcsId);
T obj = orders.stream().filter(predicate).findFirst().get();
return obj;
}
where Foo interface is
interface Foo {
String getGcsId();
}
class OrderLine implements Foo {
#Override
public String getGcsId() {
return something;
}
}
class Order implements Foo {
#Override
public String getGcsId() {
return something;
}
}
You should create an Interface. See below:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class SomeFunctionsRefactored {
public static void main(String[] args) {
Order simpleOrder = new SimpleOrder();
Order lineOrder = new LineOrder();
String gcsId;
List<Order> simpleOrders = new ArrayList(Arrays.asList(simpleOrder));
List<Order> lineOrders = new ArrayList(Arrays.asList(lineOrder));
Order order = getOrder(simpleOrders, "Hi I'm a simple order");
System.out.println(order.getGcsId()); // Hi I'm a simple order
order = getOrder(lineOrders, "Hi I'm a line order");
System.out.println(order.getGcsId()); // Hi I'm a line order
}
private static Order getOrder(List<Order> orders, String gcsId) {
Predicate<Order> predicate = c -> c.getGcsId().equals(gcsId);
return orders.stream().filter(predicate).findFirst().get();
}
The interface:
interface Order {
public String getGcsId();
}
class SimpleOrder implements Order {
String gcsId = "Hi I'm a simple order";
public String getGcsId() {
return gcsId;
}
}
class LineOrder implements Order {
String gcsId = "Hi I'm a line order";
public String getGcsId() {
return gcsId;
}
}
One way that you can perform instance of test after taking list as a parameter, but your List is parameterized.
if you have anything that is parameterized,
List<Foo> fooList = new ArrayList<Foo>();
The Generics information will be erased at runtime. Instead, this is what the JVM will see
`List fooList = new ArrayList();`
This is called type erasure.
The JVM has no parameterized type information of the List (in the example) during runtime.
Since the JVM has no information of the Parameterized type on runtime,
there's no way you can do an instanceof of ArrayList.
So better you keep your both function to work.

Using Streams to filter objects [duplicate]

Suppose there is a simple enum called Type defined like this:
enum Type{
X("S1"),
Y("S2");
private String s;
private Type(String s) {
this.s = s;
}
}
Finding the correct enum for given s is trivially done with static method with for-loop (assume the method is defined inside enum), e.g.:
private static Type find(String val) {
for (Type e : Type.values()) {
if (e.s.equals(val))
return e;
}
throw new IllegalStateException(String.format("Unsupported type %s.", val));
}
I think the functional equivalent of this expressed with Stream API would be something like this:
private static Type find(String val) {
return Arrays.stream(Type.values())
.filter(e -> e.s.equals(val))
.reduce((t1, t2) -> t1)
.orElseThrow(() -> {throw new IllegalStateException(String.format("Unsupported type %s.", val));});
}
How could we write this better and simpler? This code feels coerced and not very clear. The reduce() especially seems clunky and abused as it doesn't accumulate anything, performs no calculation and always simply returns t1 (provided the filter returns one value - if it doesn't that's clearly a disaster), not to mention t2 is there superfluous and confusing. Yet I couldn't find anything in Stream API that simply somehow returns directly a T from a Stream<T>.
Is there a better way?
I would use findFirst instead:
return Arrays.stream(Type.values())
.filter(e -> e.s.equals(val))
.findFirst()
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
Though a Map could be better in this case:
enum Type{
X("S1"),
Y("S2");
private static class Holder {
static Map<String, Type> MAP = new HashMap<>();
}
private Type(String s) {
Holder.MAP.put(s, this);
}
public static Type find(String val) {
Type t = Holder.MAP.get(val);
if(t == null) {
throw new IllegalStateException(String.format("Unsupported type %s.", val));
}
return t;
}
}
I learnt this trick from this answer. Basically the class loader initializes the static classes before the enum class, which allows you to fill the Map in the enum constructor itself. Very handy !
Hope it helps ! :)
The accepted answer works well, but if you want to avoid creating a new stream with a temporary array you could use EnumSet.allOf().
EnumSet.allOf(Type.class)
.stream()
.filter(e -> e.s.equals(val))
.findFirst()
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
Arrays.stream(Type.values()).filter(v -> v.s.equals(val)).findAny().orElseThrow(...);
How about using findAny() instead of reduce?
private static Type find(String val) {
return Arrays.stream(Type.values())
.filter(e -> e.s.equals(val))
.findAny()
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
}
I think the second answer of Alexis C. (Alexis C.'s answer) is the good one in term of complexity. Instead of searching in O(n) each time you look for a code using
return Arrays.stream(Type.values())
.filter(e -> e.s.equals(val))
.findFirst()
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
you could use O(n) time at the loading of the class by putting all elements into the map, and then access to the code of the type in constant time O(1) using the map.
enum Type{
X("S1"),
Y("S2");
private final String code;
private static Map<String, Type> mapping = new HashMap<>();
static {
Arrays.stream(Type.values()).forEach(type-> mapping.put(type.getCode(), type));
}
Type(String code) {
this.code = code;
}
public String getCode() {
return code;
}
public static Type forCode(final String code) {
return mapping.get(code);
}
}
I know this question is old but I came here from a duplicate. My answer is not strictly answering the OP's question about how to solve the problem using Java Streams. Instead, this answer expands the Map-based solution proposed in the accepted answer to become more (IMHO) manageable.
So here it is: I propose to introduce a special helper class that I named EnumLookup.
Assuming the Type enumeration is slightly better written (meaningful field name + getter), I inject an EnumLookup constant to it like below:
enum Type {
X("S1"),
Y("S2");
private static final EnumLookup<Type, String> BY_CODE = EnumLookup.of(Type.class, Type::getCode, "code");
private final String code;
Type(String code) {
this.code = code;
}
public String getCode() {
return code;
}
public static EnumLookup<Type, String> byCode() {
return BY_CODE;
}
}
The usage then becomes (again, IMO) really readable:
Type type = Type.byCode().get("S1"); // returns Type.X
Optional<Type> optionalType = Type.byCode().find("S2"); // returns Optional(Type.Y)
if (Type.byCode().contains("S3")) { // returns false
// logic
}
Finally, here's the code of the EnumLookup helper class:
public final class EnumLookup<E extends Enum<E>, ID> {
private final Class<E> enumClass;
private final ImmutableMap<ID, E> valueByIdMap;
private final String idTypeName;
private EnumLookup(Class<E> enumClass, ImmutableMap<ID, E> valueByIdMap, String idTypeName) {
this.enumClass = enumClass;
this.valueByIdMap = valueByIdMap;
this.idTypeName = idTypeName;
}
public boolean contains(ID id) {
return valueByIdMap.containsKey(id);
}
public E get(ID id) {
E value = valueByIdMap.get(id);
if (value == null) {
throw new IllegalArgumentException(String.format(
"No such %s with %s: %s", enumClass.getSimpleName(), idTypeName, id
));
}
return value;
}
public Optional<E> find(ID id) {
return Optional.ofNullable(valueByIdMap.get(id));
}
//region CONSTRUCTION
public static <E extends Enum<E>, ID> EnumLookup<E, ID> of(
Class<E> enumClass, Function<E, ID> idExtractor, String idTypeName) {
ImmutableMap<ID, E> valueByIdMap = Arrays.stream(enumClass.getEnumConstants())
.collect(ImmutableMap.toImmutableMap(idExtractor, Function.identity()));
return new EnumLookup<>(enumClass, valueByIdMap, idTypeName);
}
public static <E extends Enum<E>> EnumLookup<E, String> byName(Class<E> enumClass) {
return of(enumClass, Enum::name, "enum name");
}
//endregion
}
Note that:
I used Guava's ImmutableMap here, but a regular HashMap or LinkedHashMap can be used instead.
If you mind the lack of lazy initialization in the above approach, you can delay building of the EnumLookup until byCode method is first called (e.g. using the lazy-holder idiom, like in the accepted answer)
You'd need a getter for String s, but this is the pattern I use:
private static final Map<String, Type> TYPE_MAP =
Collections.unmodifiableMap(
EnumSet.allOf(Type.class)
.stream()
.collect(Collectors.toMap(Type::getS, e -> e)));
public static Type find(String s) {
return TYPE_MAP.get(s);
}
No for loops, only streams. Quick lookup as opposed to building a stream every time the method is called.
I can't add a comment yet, so I am posting an answer to complement the above answer, just following the same idea but using java 8 approach:
public static Type find(String val) {
return Optional
.ofNullable(Holder.MAP.get(val))
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
}
You need a getter for String s.
In the example below this method is getDesc():
public static StatusManifestoType getFromValue(String value) {
return Arrays.asList(values()).stream().filter(t -> t.getDesc().equals(value)).findAny().orElse(null);
}

Why this converter needs casting?

I need to implement an enum to enum converter in java: Enum_2 > Enum_1 and I'd like to do it in generic way.
So I defined an interface:
interface LabelAware<T extends Enum> {
String getLabel();
T getObject();
}
and Enum_1:
enum Enum_1 {
A, B;
String getValue() {
return "whatever";
}
}
and Enum_2 which implements LabelAware and needs to be converted to Enum_1:
enum Enum_2 implements LabelAware<Enum_1> {
C("c", Enum_1.A), D("d", Enum_1.B);
private final String label;
private final Enum_1 object;
Enum_2(String label, Enum_1 object) {
this.label = label;
this.object = object;
}
public String getLabel() {
return label;
}
public Enum_1 getObject() {
return object;
}
}
Finally, here's a generic converter (List.ofAll() comes from javaslang):
class Converter<S extends LabelAware, D extends Enum> {
private S[] values;
Converter(S[] values) {
this.values = values;
}
D map(String label) {
return (D) List.of(values)
.find(v -> v.getLabel().equals(label))
.map(LabelAware::getObject)
.getOrElseThrow(() -> new RuntimeException(""));
}
}
And a main method:
public class Main {
public static void main(String[] args) {
System.out.println(new Converter<Enum_2, Enum_1>(Enum_2.values()).map("c").getValue());
}
}
It all compiles and runs well, however I've no idea why I need to cast the result of Converter.map method to D, since I've declared D to extend Enum. Can it be done in a generic way without any warnings?
As a general rule, all warnings related to generics should be handled to have a safer code and avoid a warning chain (the visible warning is caused by a very far warning of the dependency chain).
But in your case, you have not a warning chain problem since externally, LabelAware is safe. LabelAware has only a internal warning (in its implementation) as Enum in extends Enum is raw-declared.
Here, a single missing generic declaration explains why the cast in Converter.map() method is not safe : Converter class declaration doesn't specify the generic for LabelAware.
You declare Converter class as :
class Converter<S extends LabelAware, D extends Enum> {
with its value field of type S:
private S[] values;
and its map() method as :
D map(String label) {
return (D) List.of(values)
.find(v -> v.getLabel().equals(label))
.map(LabelAware::getObject)
.getOrElseThrow(() -> new RuntimeException(""));
}
In map(), here .find(v -> v.getLabel().equals(label)), your retrieve so a S instance and you declared that S extends LabelAware.
Therefore finally, your retrieve an instance of LabelAware or extending it.
And LabelAware is typed with Enum generic :
interface LabelAware<T extends Enum> {
String getLabel();
T getObject();
}
So, in map() method when .map(LabelAware::getObject) is called, you retrieve a Enum type .
And an Enum type is not necessarily a D type, while the reverse is true.
Therefore, if you want to avoid the cast (and the related warning) in map(), you should specify that the generic type returned by getObject() is an instance of D by typing LabelAware with D generic :
class Converter<S extends LabelAware<D>, D extends Enum> {
You have been using raw types at several places (not only the one that yshavit pointed out in the comment). Particularly, the
class Converter<S extends LabelAware, D extends Enum>
has to be
class Converter<S extends LabelAware<D>, D extends Enum<D>>
The following should compile without warnings:
import javaslang.collection.List;
interface LabelAware<T extends Enum<?>>
{
String getLabel();
T getObject();
}
enum Enum_1
{
A, B;
String getValue()
{
return "whatever";
}
}
enum Enum_2 implements LabelAware<Enum_1>
{
C("c", Enum_1.A), D("d", Enum_1.B);
private final String label;
private final Enum_1 object;
Enum_2(String label, Enum_1 object)
{
this.label = label;
this.object = object;
}
public String getLabel()
{
return label;
}
public Enum_1 getObject()
{
return object;
}
}
class Converter<S extends LabelAware<D>, D extends Enum<D>>
{
private S[] values;
Converter(S[] values)
{
this.values = values;
}
D map(String label)
{
return List.of(values)
.find(v -> v.getLabel().equals(label))
.map(LabelAware::getObject)
.getOrElseThrow(() -> new RuntimeException(""));
}
}
(EDIT: This only tells you how to fix the problem, pragmatically. See the answer by davidxxx for details about what went wrong there, and don't forget to leave a +1 there :-))

Finding enum value with Java 8 Stream API

Suppose there is a simple enum called Type defined like this:
enum Type{
X("S1"),
Y("S2");
private String s;
private Type(String s) {
this.s = s;
}
}
Finding the correct enum for given s is trivially done with static method with for-loop (assume the method is defined inside enum), e.g.:
private static Type find(String val) {
for (Type e : Type.values()) {
if (e.s.equals(val))
return e;
}
throw new IllegalStateException(String.format("Unsupported type %s.", val));
}
I think the functional equivalent of this expressed with Stream API would be something like this:
private static Type find(String val) {
return Arrays.stream(Type.values())
.filter(e -> e.s.equals(val))
.reduce((t1, t2) -> t1)
.orElseThrow(() -> {throw new IllegalStateException(String.format("Unsupported type %s.", val));});
}
How could we write this better and simpler? This code feels coerced and not very clear. The reduce() especially seems clunky and abused as it doesn't accumulate anything, performs no calculation and always simply returns t1 (provided the filter returns one value - if it doesn't that's clearly a disaster), not to mention t2 is there superfluous and confusing. Yet I couldn't find anything in Stream API that simply somehow returns directly a T from a Stream<T>.
Is there a better way?
I would use findFirst instead:
return Arrays.stream(Type.values())
.filter(e -> e.s.equals(val))
.findFirst()
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
Though a Map could be better in this case:
enum Type{
X("S1"),
Y("S2");
private static class Holder {
static Map<String, Type> MAP = new HashMap<>();
}
private Type(String s) {
Holder.MAP.put(s, this);
}
public static Type find(String val) {
Type t = Holder.MAP.get(val);
if(t == null) {
throw new IllegalStateException(String.format("Unsupported type %s.", val));
}
return t;
}
}
I learnt this trick from this answer. Basically the class loader initializes the static classes before the enum class, which allows you to fill the Map in the enum constructor itself. Very handy !
Hope it helps ! :)
The accepted answer works well, but if you want to avoid creating a new stream with a temporary array you could use EnumSet.allOf().
EnumSet.allOf(Type.class)
.stream()
.filter(e -> e.s.equals(val))
.findFirst()
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
Arrays.stream(Type.values()).filter(v -> v.s.equals(val)).findAny().orElseThrow(...);
How about using findAny() instead of reduce?
private static Type find(String val) {
return Arrays.stream(Type.values())
.filter(e -> e.s.equals(val))
.findAny()
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
}
I think the second answer of Alexis C. (Alexis C.'s answer) is the good one in term of complexity. Instead of searching in O(n) each time you look for a code using
return Arrays.stream(Type.values())
.filter(e -> e.s.equals(val))
.findFirst()
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
you could use O(n) time at the loading of the class by putting all elements into the map, and then access to the code of the type in constant time O(1) using the map.
enum Type{
X("S1"),
Y("S2");
private final String code;
private static Map<String, Type> mapping = new HashMap<>();
static {
Arrays.stream(Type.values()).forEach(type-> mapping.put(type.getCode(), type));
}
Type(String code) {
this.code = code;
}
public String getCode() {
return code;
}
public static Type forCode(final String code) {
return mapping.get(code);
}
}
I know this question is old but I came here from a duplicate. My answer is not strictly answering the OP's question about how to solve the problem using Java Streams. Instead, this answer expands the Map-based solution proposed in the accepted answer to become more (IMHO) manageable.
So here it is: I propose to introduce a special helper class that I named EnumLookup.
Assuming the Type enumeration is slightly better written (meaningful field name + getter), I inject an EnumLookup constant to it like below:
enum Type {
X("S1"),
Y("S2");
private static final EnumLookup<Type, String> BY_CODE = EnumLookup.of(Type.class, Type::getCode, "code");
private final String code;
Type(String code) {
this.code = code;
}
public String getCode() {
return code;
}
public static EnumLookup<Type, String> byCode() {
return BY_CODE;
}
}
The usage then becomes (again, IMO) really readable:
Type type = Type.byCode().get("S1"); // returns Type.X
Optional<Type> optionalType = Type.byCode().find("S2"); // returns Optional(Type.Y)
if (Type.byCode().contains("S3")) { // returns false
// logic
}
Finally, here's the code of the EnumLookup helper class:
public final class EnumLookup<E extends Enum<E>, ID> {
private final Class<E> enumClass;
private final ImmutableMap<ID, E> valueByIdMap;
private final String idTypeName;
private EnumLookup(Class<E> enumClass, ImmutableMap<ID, E> valueByIdMap, String idTypeName) {
this.enumClass = enumClass;
this.valueByIdMap = valueByIdMap;
this.idTypeName = idTypeName;
}
public boolean contains(ID id) {
return valueByIdMap.containsKey(id);
}
public E get(ID id) {
E value = valueByIdMap.get(id);
if (value == null) {
throw new IllegalArgumentException(String.format(
"No such %s with %s: %s", enumClass.getSimpleName(), idTypeName, id
));
}
return value;
}
public Optional<E> find(ID id) {
return Optional.ofNullable(valueByIdMap.get(id));
}
//region CONSTRUCTION
public static <E extends Enum<E>, ID> EnumLookup<E, ID> of(
Class<E> enumClass, Function<E, ID> idExtractor, String idTypeName) {
ImmutableMap<ID, E> valueByIdMap = Arrays.stream(enumClass.getEnumConstants())
.collect(ImmutableMap.toImmutableMap(idExtractor, Function.identity()));
return new EnumLookup<>(enumClass, valueByIdMap, idTypeName);
}
public static <E extends Enum<E>> EnumLookup<E, String> byName(Class<E> enumClass) {
return of(enumClass, Enum::name, "enum name");
}
//endregion
}
Note that:
I used Guava's ImmutableMap here, but a regular HashMap or LinkedHashMap can be used instead.
If you mind the lack of lazy initialization in the above approach, you can delay building of the EnumLookup until byCode method is first called (e.g. using the lazy-holder idiom, like in the accepted answer)
You'd need a getter for String s, but this is the pattern I use:
private static final Map<String, Type> TYPE_MAP =
Collections.unmodifiableMap(
EnumSet.allOf(Type.class)
.stream()
.collect(Collectors.toMap(Type::getS, e -> e)));
public static Type find(String s) {
return TYPE_MAP.get(s);
}
No for loops, only streams. Quick lookup as opposed to building a stream every time the method is called.
I can't add a comment yet, so I am posting an answer to complement the above answer, just following the same idea but using java 8 approach:
public static Type find(String val) {
return Optional
.ofNullable(Holder.MAP.get(val))
.orElseThrow(() -> new IllegalStateException(String.format("Unsupported type %s.", val)));
}
You need a getter for String s.
In the example below this method is getDesc():
public static StatusManifestoType getFromValue(String value) {
return Arrays.asList(values()).stream().filter(t -> t.getDesc().equals(value)).findAny().orElse(null);
}

Categories

Resources