Get enum by its inner field - java

Have enum with inner fields, kind of map.
Now I need to get enum by its inner field.
Wrote this:
package test;
/**
* Test enum to test enum =)
*/
public enum TestEnum {
ONE(1), TWO(2), THREE(3);
private int number;
TestEnum(int number) {
this.number = number;
}
public TestEnum findByKey(int i) {
TestEnum[] testEnums = TestEnum.values();
for (TestEnum testEnum : testEnums) {
if (testEnum.number == i) {
return testEnum;
}
}
return null;
}
}
But it's not very efficient to look up through all enums each time I need to find appropriate instance.
Is there any other way to do the same?

You can use a static Map<Integer,TestEnum> with a static initializer that populates it with the TestEnum values keyed by their number fields.
Note that findByKey has been made static, and number has also been made final.
import java.util.*;
public enum TestEnum {
ONE(1), TWO(2), SIXTY_NINE(69);
private final int number;
TestEnum(int number) {
this.number = number;
}
private static final Map<Integer,TestEnum> map;
static {
map = new HashMap<Integer,TestEnum>();
for (TestEnum v : TestEnum.values()) {
map.put(v.number, v);
}
}
public static TestEnum findByKey(int i) {
return map.get(i);
}
public static void main(String[] args) {
System.out.println(TestEnum.findByKey(69)); // prints "SIXTY_NINE"
System.out.println(
TestEnum.values() == TestEnum.values()
); // prints "false"
}
}
You can now expect findByKey to be a O(1) operation.
References
JLS 8.7 Static initializers
JLS 8.9 Enums
Related questions
Static initalizer in Java
How to Initialise a static Map in Java
Note on values()
The second println statement in the main method is revealing: values() returns a newly allocated array with every invokation! The original O(N) solution could do a little better by only calling values() once and caching the array, but that solution would still be O(N) on average.

Although someone has suggested using Map<Integer, TestEnum> think twice about it.
Your original solution, especially for small enums, may be magnitudes faster than using HashMap.
HashMap will probably be not faster until your enum contains at least 30 to 40 elements.
This is one case of "If it ain't broken, don't fix it".

Here is the most convenient way to find enum value by its field:
public enum TestEnum {
A("EXAMPLE_1", "Qwerty", 1),
B("EXAMPLE_2", "Asdfgh", 2),
C("EXAMPLE_3", "Zxcvbn", 3);
private final String code;
private final String name;
private final Integer typeID;
TestEnum(String code, String name, Integer typeID) {
this.code = code;
this.name = name;
this.key = typeID;
}
public String getCode() {
return code;
}
public String getName() {
return name;
}
public Integer getKey() {
return key;
}
public static TestEnum findValueByTypeId(Integer key) {
return Arrays.stream(TestEnum.values()).filter(v ->
v.getKey().equals(key)).findFirst().orElseThrow(() ->
new Exception(String.format("Unknown TestEnum.key: '%s'", key)));
}
}

You should have a HashMap with the numbers as keys and the enum values as values.
This map can typically be in your repository. Then you can easily replace an int variable from the database with your preferred enum value.
If your keys (int values) are stored in a database, then I will say its bad design to carry those keys around in an enum on your business layer. If that's the case, I will recommend not to store the int value in the enum.

One solution is to add
public final Test[] TESTS = { null, ONE, TWO, THREE };
public static Test getByNumber(int i) {
return TESTS[i];
}
To the enum.
If the internal data is not an integer, you could have a Map which you populate in a static { ... } initializer. This map could later be used in the getByNumber method above.

Related

Reducing cyclomatic complexity

I have a class with some 20+ fields of the same type that are populated during different stages of the object lifecycle.
One of the class methods should return the field value based on the field name.
So far I have something like this:
public String getFieldValue(String fieldName){
switch (fieldName.toLowerCase(){
case "id": return getId();
case "name": return getName();
.....
the problem with this is high cyclomatic complexity.
What would be the easiest way to tackle this?
Edit: Thanks to #Filippo Possenti for his comment
Instead of a switch, you can use a Map.
Here is an example.
static interface C {
String getA();
String getB();
String getC();
}
#FunctionalInterface
static interface FieldGetter {
String get(C c);
}
static Map<String, FieldGetter> fields = Map.of(
"a", C::getA,
"b", C::getB,
"c", C::getC
);
static String getField(C object, String fieldNameToRetrieve) {
var getter = fields.get(fieldNameToRetrieve);
if(getter == null) {
throw new IllegalArgumentException("unknown field");
}
return getter.get(object);
}
Why don't you use reflexion or an existing library for this ? (Or why do you even have this kind of method)
In theory you could reduce the getFieldValue() method complexity by:
storing the getter method reference as Producer<?> in Map<String, Producer<?>>
using reflection to lookup fields
using 3rd party library that supports querying the bean by property name e.g. commons-beanutils.
Each of these approaches will however increase the getFieldValue() method complexity and potentially reduce the performance. Both are worse problems than high complexity.
It feels like you should review why you need the getFieldValue() method in the first place, maybe it should be a Map<String, ?>?
Assuming that the fieldName possible values match the getters on the bean, you can use Apache's BeanUtils:
https://commons.apache.org/proper/commons-beanutils/apidocs/org/apache/commons/beanutils/PropertyUtils.html#getSimpleProperty-java.lang.Object-java.lang.String-
Basically, you could do something like this:
public String getFieldValue(String fieldName){
return PropertyUtils.getSimpleProperty(fieldName.toLowerCase());
}
This is more about improving code readability than improving cyclomatic complexity so if it's pure performance what you're after, this may not be your solution.
If pure performance is what you're after, you could try and leverage lambdas and a Map.
import java.util.Map;
import java.util.HashMap;
import java.util.function.Function;
public class HelloWorld{
public static class MyClass {
private static Map<String, Function<MyClass, Object>> descriptor;
static {
descriptor = new HashMap<>();
descriptor.put("id", MyClass::getId);
descriptor.put("name", MyClass::getName);
}
private String id;
private String name;
public String getId() {
return id;
}
public String getName() {
return name;
}
public void setId(String value) {
id = value;
}
public void setName(String value) {
name = value;
}
public Object getFieldValue(String fieldName) {
Function fn = descriptor.get(fieldName);
return fn.apply(this);
}
}
public static void main(String []args){
MyClass mc = new MyClass();
mc.setId("hello");
mc.setName("world");
System.out.println(mc.getFieldValue("id") + " " + mc.getFieldValue("name"));
}
}
To note that in the above example the cyclomatic complexity is somewhat still there, but it's moved in the class' static initialiser. This means that you'll suffer a modest penalty during application startup but enjoy higher performance in subsequent calls of getFieldValue.
Also, if performance is what you're after you may want to eliminate the need for toLowerCase... which in my example I removed.
Instead of the switch or using a Map, you can use an enum.
enum FieldExtractor implements Function<YourClass, String> {
ID(YourClass::getId),
NAME(YourClass::getName); // and so on
private final Function<YourClass, String> delegate;
FieldExtractor(Function<YourClass, String> delegate) {
this.delegate = delegate;
}
#Override public String apply(YourClass extractFrom) {
return delegate.apply(extractFrom);
}
static FieldExtractor fromString(String name) {
return Stream.of(FieldExtractor.values())
.filter(fe -> fe.name().equalsIgnoreCase(name))
.findFirst()
.orElseThrow(IllegalArgumentException::new);
}
}
Now you can use
public String getFieldValue(String fieldName) {
return FieldExtractor.fromString(fieldName).apply(this);
}
in your client code.

Java enum reverse lookup

So I have this enum that doesn't work as I expected and need some modifications:
public enum MyEnum {
CODE000("text description comes here"),
private final String value;
private static final Map<String, MyEnum> LOOKUP = Maps.uniqueIndex(
Arrays.asList(MyEnum.values()),
MyEnum::getValue
);
MyEnum(final String value) {
this.value = value;
}
public String getValue() {
return value;
}
public static MyEnum fromStatus(String status) {
return LOOKUP.get(status);
}
}
The way it works now is:
MyEnum.fromStatus("text description comes here") and of course I want the other way around:
MyEnum.fromStatus("CODE000") to return me "text description comes here"
Can someone suggest how I can change this ?
What you need is a literal lookup, which you get by calling valueOf:
MyEnum code000 = MyEnum.valueOf("CODE000");
And then:
String val = code000.getValue();
Please note that an exception will be raised if the string passed to valueOf doesn't resolve to an enum literal in MyEnum.
Your key function (MyEnum::getValue) is wrong as it returns the value.
It must be MyEnum::name
This will return the enum and not the text description as the value of the map is of type MyEnum. You can get the text value by calling getValue on the enum OR you can store the value in the map instead of the enum
If you want to get an enum value by enum name you can use this function :
public static String fromStatus(String status) {
MyEnum myEnum = valueOf(status);
return myEnum.getValue();
}
The answers so far are using the method valueOf. This method will return the enum constant as long as you provide a name of an enum constant. Otherwise an IllegalArgumentException will be thrown.
In your question you're using a lookup map. The Map (it looks like as it's created by Guava Maps) will return for non-enum-constant-names null. It will not throw a IllegalArgumentException in such cases. So it is a different behaviour.
In addition you say: "and of course I want the other way around"
This means you want to get the enum by status and the status by an enums name.
Therefore you would need to have two lookup methods:
status -> enum
name -> status
But you would get a compile time error if you define the two methods you mentioned:
public static MyEnum fromStatus(String status) { ... }
public static String fromStatus(String name) { ... }
The compiler could not distinguish the methods by name and parameter. But even though you wrote MyEnum.fromStatus("CODE000") actually it's the enum constant name you are using as parameter. So let's resolve the naming conflict by calling the second method fromName. The code for MyEnum could look like this:
public enum MyEnum {
CODE000("text description comes here");
private final String value;
private static final Map<String, MyEnum> LOOKUP_ENUM = Maps.uniqueIndex(Arrays.asList(MyEnum.values()), MyEnum::getValue);
private static final Map<String, String> LOOKUP_STATUS = Arrays.stream(MyEnum.values()).collect(Collectors.toMap(MyEnum::name, MyEnum::getValue));
MyEnum(final String value) {
this.value = value;
}
public String getValue() {
return value;
}
public static MyEnum fromStatus(String status) {
return LOOKUP_ENUM.get(status);
}
public static String fromName(String name) {
return LOOKUP_STATUS.get(name);
}
}
If you want to lookup the enum constants by it's names in the same manner (no exception on non-enum-constant-names), you need a third map and a third lookup method:
private static final Map<String, MyEnum> LOOKUP = Maps.uniqueIndex(Arrays.asList(MyEnum.values()), MyEnum::name);
public static MyEnum byName(String name) {
return LOOKUP.get(name);
}
This would work as follows:
System.out.println(MyEnum.fromStatus("text description comes here")); // CODE000
System.out.println(MyEnum.fromStatus("invalid")); // null - no exception
System.out.println(MyEnum.fromStatus(null)); // null - no exception
System.out.println(MyEnum.fromName("CODE000")); // "text description comes here"
System.out.println(MyEnum.fromName("invalid")); // null - no exception
System.out.println(MyEnum.fromName(null)); // null - no exception
System.out.println(MyEnum.byName("CODE000")); // CODE000
System.out.println(MyEnum.byName("invalid")); // null - no exception
System.out.println(MyEnum.byName(null)); // null - no exception
If you need the byName method I would suggest to rename the methodfromName to something like statusByName to keep them comprehensible apart.
Finally one more suggestion:
Since the lookup methods may return null we could return Optional<String> / Optional<MyEnum> as result. This would allow to immediately continue processing the result.
public static Optional<MyEnum> fromStatus(String status) { ... }
public static Optional<String> statusByName(String name) { ... }
public static Optional<MyEnum> byName(String name) { ... }

Looking up enum label by value

I have the following enum in my java android application:
static enum PaymentType
{
Scheme(0), Topup(1), Normal(2), Free(3), Promotion(4), Discount(5), Partial(6),
Refund(7), NoShow(8), Prepay(9), Customer(10), Return(11), Change(12), PettyCash(13),
StateTax(14), LocalTax(15), Voucher(16), Membership(17), Gratuity(18), Overpayment(19),
PrepayTime(20), HandlingFee(21);
private int value;
private PaymentType(int i) {
value = i;
}
public int getValue() {
return value;
}
}
I use this enum alot to find out the integer value of one of these string labels, for example int i = Lookups.PaymentType.Voucher.getValue();.
How can I do this the other way around? I have an integer value from a database and I need to find which string that corresponds to.
You should do something like this (static-init block should be at the end! and in your case just replace "asc" and "desc" with numbers, or add any other field):
public enum SortOrder {
ASC("asc"),
DESC("desc");
private static final HashMap<String, SortOrder> MAP = new HashMap<String, SortOrder>();
private String value;
private SortOrder(String value) {
this.value = value;
}
public String getValue() {
return this.value;
}
public static SortOrder getByName(String name) {
return MAP.get(name);
}
static {
for (SortOrder field : SortOrder.values()) {
MAP.put(field.getValue(), field);
}
}
}
After that, just call:
SortOrder asc = SortOrder.getByName("asc");
To go from an ordinal() index value back to enum:
type = PaymentType.values()[index];
However, keep in mind that this is fragile when the ordinal is stored anywhere else, such as a database. If the index numbers ever change, you'll get invalid results.
For more reliable lookup table, use a Map.

In Java, how to iterate on the constants of an interface?

in an interface, I store constants in this way (I'd like to know what you think of this practice). This is just a dummy example.
interface HttpConstants {
/** 2XX: generally "OK" */
public static final int HTTP_OK = 200;
public static final int HTTP_CREATED = 201;
public static final int HTTP_ACCEPTED = 202;
public static final int HTTP_NOT_AUTHORITATIVE = 203;
public static final int HTTP_NO_CONTENT = 204;
public static final int HTTP_RESET = 205;
public static final int HTTP_PARTIAL = 206;
...
}
Is there a way I can iterate over all constants declared in this interface ?
Using reflection:
Field[] interfaceFields=HttpConstants.class.getFields();
for(Field f:interfaceFields) {
//do something
}
But anyway, if you can redesign your class, I would recomend you to handle a static enum constants construction. So, suposing your class will contain always an int value for every constant:
enum HttpConstants {
HTTP_OK(200), HTTP_CREATED(201), HTTP_ACCEPTED(202),
HTTP_NOT_AUTHORITATIVE(203),HTTP_NO_CONTENT(204),
HTTP_RESET(205), HTTP_PARTIAL(206) /* ... */;
private int value;
HttpConstants(int aValue) {
value=aValue;
}
public int getValue() {
return value;
}
}
Then, to loop on it:
for(HttpConstants val: HttpConstants.values()) {
int value=val.getValue();
//...
}
Thus, avoiding the access to the reflection API.
I would create these constants as an enumeration. Enums in Java can have their own fields and methods, which very convenient for your case. So I would do this the following way:
enum HttpConstant {
HTTP_OK(200),
HTTP_CREATED(201),
HTTP_ACCEPTED(202),
HTTP_NOT_AUTHORITATIVE(203),
HTTP_NO_CONTENT(204),
HTTP_RESET(205),
HTTP_PARTIAL(206);
private final int id;
HttpConstant(int id) {
this.id = id;
}
int getId() {
return id;
}
}
Now the iteration is easy:
for (HttpConstant constant : HttpConstant.values()) {
//Do something with the constant
}
This way it is also easy to add associate some new values with the constants, you just have to add new fields.
Right now you may use reflection:
Field[] interfaceFields = HttpConstants.class.getFields();
for (Field field : interfaceFields) {
int constant = field.getInt(null);
//Do something with the field
}
However, it is better to use the approach with enums because with reflection coding errors result in runtime exceptions instead of compile-time errors.
for(Field f : HttpConstants.class.getFields()){
int constant = f.getInt(null);
}
public enum HttpConstant {
/** 2XX: generally "OK" */
HTTP_OK(200).
HTTP_CREATED(201),
HTTP_ACCEPTED(202),
HTTP_NOT_AUTHORITATIVE(203),
HTTP_NO_CONTENT(204),
HTTP_RESET(205),
HTTP_PARTIAL(206);
private int code;
private HttpConstant(int code) {
this.code = code;
}
public int getCode() {
return code;
}
}
with HttpConstant.values().
Well usually when i have something like that i make a Map in the interface that has the keys - constant names with values constant - values.
And that's how i can iterate over them.
I'd like to know what you think of this practice
Consider using an enum instead of an interface with constants.
enum HttpResultCode {
HTTP_OK(200),
HTTP_CREATED(201),
HTTP_ACCEPTED(202),
HTTP_NOT_AUTHORITATIVE(203),
HTTP_NO_CONTENT(204),
HTTP_RESET(205),
HTTP_PARTIAL(206);
private final int code;
private HttpResultCode(int code) {
this.code = code;
}
public int getCode(int code) {
return code;
}
public static HttpResultCode forCode(int code) {
for (HttpResultCode e : HttpResultCode.values()) {
if (e.code == code) {
return e;
}
}
throw new IllegalArgumentException("Invalid code: " + code);
}
}

Design Issue | Enum to represent combo box options

I need to use an Enum with a combobox (values shown below).
YES (shown as YES on UI, stored in DB as Y)
NO (shown as NO on UI, stored in DB as N)
DEFAULT (shown as "" on UI, stored in DB as null)
The Enum has methods to perform the following -
toString() - to provide the custom String for UI. (showing the combo options)
OptionToDB (static) - Convert a selected option to db value (on save / update)
DBToOption (static)- Convert a DB value to selcted option (while loading the screen)
static enum EnumOption{
YES,NO,DEFAULT;
....
public static EnumOption DBToOption(String val){
if("Y".equals(val)){
return YES;
} else if("N".equals(val)){
return NO;
}else {
return DEFAULT;
}
}
....
}
It works pretty well, but the issue with above methods is that it uses if/else comparison to deduce which option / db value to be returned.
I thought of storing the dbValue as a field in enum but I was not able to reduce the if/else from DBToOption.
Can this if/else be avoided in any way using a better design??
If you store the dbValue as a field in the enum, you can remove the if/else and replace it with a for-loop, although I don't see anything wrong with those if/elses for this particular case:
static enum EnumOption {
YES("Y"),
NO("N"),
DEFAULT("");
private final String value;
private EnumOption(String value) {
this.value = value;
}
public static EnumOption DBToOption(String val) {
for (EnumOption opt : EnumOption.values()) {
if (opt.value.equals(val)) {
return opt;
}
}
return DEFAULT;
}
}
public enum EnumOption {
YES("Y"), NO("N"), DEFAULT("");
private final String value;
private final static Map<String, EnumOption> options;
static {
options = new HashMap<String, EnumOption>();
for (EnumOption opt : EnumOption.values()) {
options.put(opt.value, opt);
}
}
private EnumOption(String value) {
this.value = value;
}
public static EnumOption DBToOption(String val) {
return options.get(val) != null ? options.get(val) : DEFAULT;
}
}
And here is the test that proves it works.
public void testDBToOption() {
assertEquals(EnumOption.NO, EnumOption.DBToOption("N"));
assertEquals(EnumOption.YES, EnumOption.DBToOption("Y"));
assertEquals(EnumOption.DEFAULT, EnumOption.DBToOption(""));
assertEquals(EnumOption.DEFAULT, EnumOption.DBToOption(null));
assertEquals(EnumOption.DEFAULT, EnumOption.DBToOption("R"));
}
So you want to get rid of the remaining if/else ...Are you doing Object Calisthenics?
You could do the following, if you do not have compatibility issues:
public enum EnumOption {
Y("Y", "YES"),
N("N", "NO"),
D("D", "");
private final String dbValue;
private final String uiValue;
private EnumOption(String dbValue, String uiValue) {
this.dbValue = dbValue;
this.uiValue = uiValue;
}
public String getDbValue() {
return this.dbValue;
}
public String uiValue() {
return this.uiValue;
}
public static EnumOption getFromDb(String dbValue) {
return EnumOption.valueOf(dbValue);
}
}
Since each enum value can only occur once, this has at least the same performance as all the other implementations.
For details about the automatically generated valueOf(String) method in enum types, and James DW's solution, you can read up in Josh Bloch's Effective Java Item 30 (Use enums instead of int constants), page 154.

Categories

Resources