Optimize java enum - java

My code contain multiple enum like below. Basically that help to use enum via integer instead of enum value. Is it possible apply some sort of optimization like inheritance or something so that all can have behavior like below.
public enum DeliveryMethods {
STANDARD_DOMESTIC(1), STANDARD_INTERNATIONAL(2), EXPRESS_DOMESTIC(3), EXPRESS_INTERNATIONAL(4);
private final int code;
private DeliveryMethods(int code) {
this.code = code;
}
public int getCode() {
return code;
}
private static final HashMap<Integer, DeliveryMethods> valueMap = new HashMap<>(2);
static {
for (DeliveryMethods type : DeliveryMethods.values()) {
valueMap.put(type.code, type);
}
}
public static DeliveryMethods getValue(int code) {
return valueMap.get(code);
}
}

Here is an example showing how you could delegate to another class:
public interface Keyed<K> {
/**
* returns the key of the enum
*/
K getKey();
}
public class KeyEnumMapping<K, E extends Enum<?> & Keyed<K>> {
private Map<K, E> map = new HashMap<>();
public KeyEnumMapping(Class<E> clazz) {
E[] enumConstants = clazz.getEnumConstants();
for (E e : enumConstants) {
map.put(e.getKey(), e);
}
}
public E get(K key) {
return map.get(key);
}
}
public enum Example implements Keyed<Integer> {
A(1),
B(3),
C(7);
private static final KeyEnumMapping<Integer, Example> MAPPING = new KeyEnumMapping<>(Example.class);
private Integer value;
Example(Integer value) {
this.value = value;
}
#Override
public Integer getKey() {
return value;
}
public static Example getByValue(Integer value) {
return MAPPING.get(value);
}
public static void main(String[] args) {
System.out.println(Example.getByValue(3));
}
}
You could also avoid implementing a Keyed interface and simply pass a Function<E, K> to KeyEnumMapping constructor, that would transform the enum into its key:
public class KeyEnumMapping<K, E extends Enum<?>> {
private Map<K, E> map = new HashMap<>();
public KeyEnumMapping(Class<E> clazz, Function<E, K> keyExtractor) {
E[] enumConstants = clazz.getEnumConstants();
for (E e : enumConstants) {
map.put(keyExtractor.apply(e), e);
}
}
public E get(K key) {
return map.get(key);
}
}
public enum Example {
A(1),
B(3),
C(7);
private static final KeyEnumMapping<Integer, Example> MAPPING =
new KeyEnumMapping<>(Example.class, Example::getValue);
private Integer value;
Example(Integer value) {
this.value = value;
}
public Integer getValue() {
return value;
}
public static Example getByValue(Integer value) {
return MAPPING.get(value);
}
public static void main(String[] args) {
System.out.println(Example.getByValue(3));
}
}

You can consider using the getOrdinal() method of Enum instead of maintaining the 'code' yourself.
http://docs.oracle.com/javase/7/docs/api/java/lang/Enum.html#ordinal()
Even if you maintain the 'code' attribute it is not necessary to maintain the 'valueMap'. Instead you can use the 'values()' method of Enum and iterate over all the enums.

There is no need for Hashmap unless until it is necessary.It's better to go with switch-case for enum values
I've written to get enum from Integer as well as string
public enum DeliveryMethods {
STANDARD_DOMESTIC(1), STANDARD_INTERNATIONAL(2), EXPRESS_DOMESTIC(3), EXPRESS_INTERNATIONAL(4);
private final int code;
private DeliveryMethods(int code) {
this.code = code;
}
public int getCode() {
return code;
}
public static DeliveryMethods fromString(String code) {
if (code.matches("[1-4]")) {
return fromInteger(Integer.valueOf(code));
}
throw new RuntimeException("No values for code " + code);
}
public static DeliveryMethods fromInteger(int code) {
switch (code) {
case 1:
return STANDARD_DOMESTIC;
case 2:
return STANDARD_INTERNATIONAL;
case 3:
return EXPRESS_DOMESTIC;
case 4:
return EXPRESS_INTERNATIONAL;
}
throw new RuntimeException("No values for code " + code);
}
public static DeliveryMethods fromIntegerType2(int code) {
for (DeliveryMethods d : DeliveryMethods.values()) {
if (d.getCode() == code) {
return d;
}
}
throw new RuntimeException("No values for code " + code);
}
}

Related

Same methods, different enums, how to design a parent class?

My two Type classes called SearchType and ResultcodeType need a parent class in an elegant way. How to design these two classes and a parent class both inherit from in an clean and code saving way?
public enum SearchType {
BARCODE(0),
TEXT(1);
SearchType(int i)
{
this.type = i;
}
private int type;
public static SearchType getType(int value) {
for (SearchType searchType : SearchType.values()) {
if (searchType.type == value)
return searchType;
}
throw new IllegalArgumentException("SearchType not found.");
}
public int getNumericType() {
return type;
}
}
and
public enum ResultcodeType {
RESULTS(0),
NO_RESULTS(1),
PROBLEMS(2),
NO_VALUE(-1);
ResultcodeType(int i)
{
this.type = i;
}
private int type;
public static ResultcodeType getType(int value) {
for (ResultcodeType resultcodeType : ResultcodeType.values()) {
if (resultcodeType.type == value)
return resultcodeType;
}
throw new IllegalArgumentException("ResultcodeType not found.");
}
public int getNumericType() {
return type;
}
}
Where do I use SearchType / ResultCodeType?
Layout Data Binding
<ImageView
app:srcCompat="#{item.searchType == SearchType.BARCODE ? #drawable/ic_barcode : #drawable/ic_one_loupe}"
/>
Room database converter class (where there is redundancy again). But for now room can't handle generic types in it's TypeConverter. So this will stay as is.
#TypeConverter
public static SearchType SearchTypeFromInt(Integer value) {
return SearchType.getType(value);
}
#TypeConverter
public static ResultcodeType ResultcodeTypeFromInt(Integer value) {
return ResultcodeType.getType(value);
}
POJO (with room annotation)
#NonNull
#ColumnInfo(name = "resultcode", defaultValue="-1")
private ResultcodeType mResultcode;
Since enums cannot have base classes, I think this is the closest you're going to get:
public interface Typed {
int getNumericType();
static <E extends Enum<E> & Typed> E getType(E[] values, int type) {
for (E value : values)
if (value.getNumericType() == type)
return value;
throw new IllegalArgumentException(values[0].getClass().getSimpleName() +
" not found: " + type);
}
}
public enum SearchType implements Typed {
BARCODE(0),
TEXT(1);
private final int type;
private SearchType(int type) {
this.type = type;
}
#Override
public int getNumericType() {
return this.type;
}
public static SearchType getType(int type) {
return Typed.getType(values(), type);
}
}
public enum ResultcodeType implements Typed {
RESULTS(0),
NO_RESULTS(1),
PROBLEMS(2),
NO_VALUE(-1);
private final int type;
private ResultcodeType(int type) {
this.type = type;
}
#Override
public int getNumericType() {
return this.type;
}
public static ResultcodeType getType(int type) {
return Typed.getType(values(), type);
}
}
Your enums could implement an interface and add default method.
For example:
interface Typed {
Typed getType(int value)
public enum ResultcodeType implements Typed {
public Typed getType(int value) {
for (ResultcodeType resultcodeType :
ResultcodeType.values()) {
if (resultcodeType.type == value)
return resultcodeType;
}
throw new IllegalArgumentException("ResultcodeType not found.");
}
....
}
I also suggest the following approach using a map instead of searching. In fact, all you need is the mapping. You wouldn't even need to supply a value. Note that you can't reference a static value from within a constructor so you have to build the map externally.
enum SearchType {
BARCODE(0), TEXT(1), UNKNOWN(-1);
static Map<Integer, SearchType> map =
Map.of(0, SearchType.BARCODE, 1, SearchType.TEXT);
SearchType(int i) {
this.type = i;
}
private int type;
public static SearchType getType(int value) {
return SearchType.map.getOrDefault(value, SearchType.UNKNOWN);
}
public int getNumericType() {
return type;
}
}
public static void main(String[] args) {
System.out.println(SearchType.getType(0));
System.out.println(SearchType.getType(1));
System.out.println(SearchType.getType(99));
}

How to return Enum if matches with given String

I have Enum class as given below
public enum AlgorithmEnum {
SHA512("RSA", "SHA512", 1), SHA1("RSA", "SHA1", 1), SHA384("RSA", "SHA384", 1);
private String keyAlgorithm;
private String hashAlgorithm;
private Integer key;
private AlgorithmEnum(String keyAlgorithm, String hashAlgorithm, Integer key) {
this.keyAlgorithm = keyAlgorithm;
this.hashAlgorithm = hashAlgorithm;
this.key = key;
}
public String getKeyAlgorithm() {
return keyAlgorithm;
}
public void setKeyAlgorithm(String keyAlgorithm) {
this.keyAlgorithm = keyAlgorithm;
}
public String getHashAlgorithm() {
return hashAlgorithm;
}
public void setHashAlgorithm(String hashAlgorithm) {
this.hashAlgorithm = hashAlgorithm;
}
public Integer getKey() {
return key;
}
public void setKey(Integer key) {
this.key = key;
}
}
I need to have method something like below which takes input as string and returns Enum
public AlgorithmEnum getAlgorithm(String algorithm){
//returns AlgorithmEnum object
}
I would call above method by passing "SHA512withRSA" as input for getAlgorithm method.
I need help in implementing the getAlgorithm method.
You can have something like:
public static AlgorithmEnum getAlgorithm(final String algorithm)
throws IllegalArgumentException
{
for (final AlgorithmEnum algorithmEnum : AlgorithmEnum.values())
{
if (algorithm.equalsIgnoreCase(String.format("%swith%s", algorithmEnum.getHashAlgorithm(), algorithmEnum.getKeyAlgorithm())))
{
return algorithmEnum;
}
}
throw new IllegalArgumentException("Unknown algorithm: " + algorithm);
}
However, I will not suggest to use this approach. Instead use 2 different arguments instead of a single String.
Assuming all string values passed to your method getAlgorithm() end with withRSA you could use the following to fetch the enum values :
public AlgorithmEnum getAlgorithm(String algorithm) {
return AlgorithmEnum.valueOf(algorithm.substring(0, algorithm.indexOf("withRSA")));
}
You can check if the given String contains a value that matches one of the enum attributes with some if statements:
public AlgorithmEnum getAlgorithm(String algorithm) {
if (algorithm.contains("SHA1")) {
return SHA1;
} else if (algorithm.contains("SHA512")) {
return SHA512;
} else if (algorithm.contains("SHA384")) {
return SHA384;
} else {
return null;
}
}
Please note that this will match Strings like "SHA512withoutRSA", too...
Maybe a method like
public AlgorithmEnum getAlgorithm(String keyAlgorithm, String hashAlgorithm)
would be better. However, you would have to provide two parameters then.
I'm leaving you an example of how I did on similar cases, you can easily adapt it to your needs:
private static Map<Integer, YourEnum> valuesById = new HashMap<>();
private static Map<String, YourEnum> valuesByCode = new HashMap<>();
static {
Arrays.stream(YourEnum.values()).forEach(value -> valuesById.put(value.reasonId, value));
Arrays.stream(YourEnum.values()).forEach(value -> valuesByCode.put(value.reasonCode, value));
}
public static YourEnum getByReasonId(int endReason) {
return valuesById.get(endReason);
}

how to convert "value" to enum?

I have this enum class:
public enum IconImageTag {
None("val1"),
USD("val2"),
EURO("val3");
}
given a string which represent a "value" (say `"val"1)
how can I convert it to the corresponding enum?
update
I have tried this. Why is this illegal to access static member from the ctor? I get an error.
private final String value;
private static final Map<String, IconImageTag> stringToEnumMap = new HashMap<>();
IconImageTag(String value) {
this.value = value;
stringToEnumMap.put(value, this);
}
Ideally, you'd build up a Map<String, IconImageTag> and add an appropriate method. For example:
public enum IconImageTag {
NONE("val1"),
USD("val2"),
EURO("val3");
private final String value;
private final Map<String, IconImageTag> valueMap = new HashMap<>();
static {
for (IconImageTag tag : values()) {
valueMap.put(tag.value, tag);
}
}
private IconImageTag(String value) {
this.value = value;
}
public static IconImageTag fromValue(String value) {
return valueMap.get(value);
}
}
(I'd probably use a different term from "value" here, to avoid confusion with valueOf() etc...)
Note the use of the static initializer block - any static variables in an enum are initialized after the enum values themselves, which means that when the enum constructor runs, valueMap will still be null.
You can also iterate over every enum.
public enum IconImageTag {
None("val1"),
USD("val2"),
EURO("val3");
private final String value;
private IconImageTag(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public static IconImageTag getByValue(String value) {
for(IconImageTag iconImageTag : values()) {
if(iconImageTag.getValue().equals(value)) {
return iconImageTag;
}
}
return null;
}

java generics T extends Simpletype?

I'd like to write a method, that does return something of a PrimitiveType like float, integer, boolean and also String if possible. I'd like to use generics for it but i stuck and dont find a solution for it. I do need it for a Configparser. Ill use it to get different values from the Config.
Current it des look like this and i know that the switch does not work like this but you get an idea of what id like to do:
public class ConfigurationManager extends XmlReader {
private final static String FILE_PATH = "config/config.cfg";
private static Element xml;
public ConfigurationManager() throws IOException {
FileHandle handle = Gdx.files.internal(FILE_PATH);
this.xml = this.parse(handle);
}
public Resolution getResolution() {
Resolution r = new Resolution();
r.height = xml.getFloat("height");
r.width = xml.getFloat("width");
return r;
}
public static <T> T getConfig(Class<T> type, String name) {
if (type.equals(Integer.class)) {
return type.cast(xml.getInt(name));
} else if (type.equals(Float.class)) {
return type.cast(xml.getFloat(name));
} else if (type.equals(Boolean.class)) {
return type.cast(xml.getBoolean(name));
} else if (type.equals(String.class)) {
return type.cast(xml.get(name));
}
throw new AssertionError("Invalid type");
}
}
Thanks alot
Well, I don't think you can do it with primitive types directly, but how about something like this:
public static <T> T getConfig(Class<T> type, String name) {
if(type.equals(Integer.class)){
return type.cast(xml.getInteger(name));
} else if(type.equals(Float.class)){
return type.cast(xml.getFloat(name));
} else if(type.equals(Double.class)) {
return type.cast(xml.getDouble(name));
} else if(type.equals(String.class)) {
return type.cast(xml.getString(name));
}
throw new AssertionError("Invalid type");
}
You could use an Enum to avoid the branching logic and the explicit casting.
public enum TypeSelector {
INTEGER() {
#Override
public Integer getValue(Elements xml, String name) {
return xml.getInteger(name);
}
},
DOUBLE() {
#Override
public Double getValue(Elements xml, String name) {
return xml.getDouble(name);
}
};
private static final Map<Class<?>, TypeSelector> SELECTORS = new HashMap<Class<?>, TypeSelector>() {
{
put(Integer.class, INTEGER);
put(Double.class, DOUBLE);
}
};
public static <T> TypeSelector getSelectorForType(Class<T> c) {
TypeSelector selector = SELECTORS.get(c);
if (selector == null) {
throw new AssertionError("Invalid type");
}
return selector;
}
public abstract <T> T getValue(Elements xml, String name);
}

How to genericize a Java enum with static members?

I am refactoring a part of our legacy app which handles exporting and importing of DB tables from/to Excel sheets. We have a Formatter subclass for each table, to provide the definition of that table: how many columns it has, and what is the name, format and validator of each column. The getters which supply this data are then called by a Template Method which exports/imports the table. I have extracted the column data into an enum, which greatly simplified the code. A formatter now looks like this (some details omitted for brevity):
public class DamageChargeFormatter extends BaseFormatter {
public static final int NUM_COLUMNS = 7;
public enum Column {
VEHICLE_GROUP(0, "Vehicle Group", /* more params */),
NAME_OF_PART(1, "Name of Part", /* more params */),
//...
LOSS_OF_USE(6, "Loss of Use", /* more params */);
private static final Map<Integer, Column> intToColumn = new HashMap<Integer, Column>();
static {
for (Column type : values()) {
intToColumn.put(type.getIndex(), type);
}
}
public static TableColumn valueOf(int index) {
return intToColumn.get(index);
}
private int index;
private String name;
Column(int index, String name, /* more params */) {
this.index = index;
this.name = name;
//...
}
public int getIndex() { return index; }
public String getName() { return name; }
// more members and getters...
}
protected String getSheetName() {
return "Damage Charges";
}
public String getColumnName(int columnNumber) {
TableColumn column = Column.valueOf(columnNumber);
if (column != null) {
return column.getName();
}
return null;
}
// more getters...
protected int getNumColumns() {
return NUM_COLUMNS;
}
protected boolean isVariableColumnCount() {
return false;
}
}
Now, I have about a dozen such classes, each of which containing exactly the same code except that NUM_COLUMNS and the enum values of Column are different. Is there any way to genericize this somehow? The main obstacle to this is the static Column.valueOf() method and the static constant NUM_COLUMNS. Another concern with latter is that it really belongs to an abstraction one level higher, i.e. to the table, not to an individual column - it would be nice to somehow incorporate this into the generic solution.
Technically I could solve this with a base interface (TableColumn below) and reflection, but I don't really like that, as apart from trading compile time errors to runtime errors, it makes the code ugly (to me):
public class GenericFormatter<E extends TableColumn> extends BaseFormatter {
private Method valueOfMethod;
public GenericFormatter(Class<E> columnClass) {
try {
valueOfMethod = columnClass.getDeclaredMethod("valueOf", Integer.class);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
public String getColumnName(int columnNumber) {
try {
#SuppressWarnings("unchecked")
E elem = (E) valueOfMethod.invoke(columnNumber);
if (elem != null) {
return elem.getName();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return null;
}
//...
}
Note that this code is purely experimental, as yet untested...
Is there a nicer, cleaner, safer way?
May be, something like this:
public class TableMetadata<E extends Enum & TableColumn> {
private Map<Integer, TableColumn> columns = new HashMap<Integer, TableColumn>();
public TableMetadata(Class<E> c) {
for (E e: c.getEnumConstants()) {
columns.put(e.getIndex(), e);
}
}
public String getColumnName(int index) {
return columns.get(index).getName();
}
}
public class GenericFormatter<E extends TableColumn> extends BaseFormatter {
private TableMetadata<E> m;
public GenericFormatter(TableMetadata<E> m) {
this.m = m;
}
public String getColumnName(int columnNumber) {
return m.getColumnName(index);
}
//...
}
EDIT: Enum added to the type parameter for more compile-time safety

Categories

Resources