I have a large number of Enums that implement this interface:
/**
* Interface for an enumeration, each element of which can be uniquely identified by its code
*/
public interface CodableEnum {
/**
* Get the element with a particular code
* #param code
* #return
*/
public CodableEnum getByCode(String code);
/**
* Get the code that identifies an element of the enum
* #return
*/
public String getCode();
}
A typical example is:
public enum IMType implements CodableEnum {
MSN_MESSENGER("msn_messenger"),
GOOGLE_TALK("google_talk"),
SKYPE("skype"),
YAHOO_MESSENGER("yahoo_messenger");
private final String code;
IMType (String code) {
this.code = code;
}
public String getCode() {
return code;
}
public IMType getByCode(String code) {
for (IMType e : IMType.values()) {
if (e.getCode().equalsIgnoreCase(code)) {
return e;
}
}
}
}
As you can imagine these methods are virtually identical in all implementations of CodableEnum. I would like to eliminate this duplication, but frankly don't know how. I tried using a class such as the following:
public abstract class DefaultCodableEnum implements CodableEnum {
private final String code;
DefaultCodableEnum(String code) {
this.code = code;
}
public String getCode() {
return this.code;
}
public abstract CodableEnum getByCode(String code);
}
But this turns out to be fairly useless because:
An enum cannot extend a class
Elements of an enum (SKYPE, GOOGLE_TALK, etc.) cannot extend a class
I cannot provide a default implementation of getByCode(), because DefaultCodableEnum is not itself an Enum. I tried changing DefaultCodableEnum to extend java.lang.Enum, but this doesn't appear to be allowed.
Any suggestions that do not rely on reflection?
Thanks,
Don
You could factor the duplicated code into a CodeableEnumHelper class:
public class CodeableEnumHelper {
public static CodeableEnum getByCode(String code, CodeableEnum[] values) {
for (CodeableEnum e : values) {
if (e.getCode().equalsIgnoreCase(code)) {
return e;
}
}
return null;
}
}
Each CodeableEnum class would still have to implement a getByCode method, but the actual implementation of the method has at least been centralized to a single place.
public enum IMType implements CodeableEnum {
...
public IMType getByCode(String code) {
return (IMType)CodeableEnumHelper.getByCode(code, this.values());
}
}
Abstract enums are potentially very useful (and currently not allowed). But a proposal and prototype exists if you'd like to lobby someone in Sun to add it:
http://freddy33.blogspot.com/2007/11/abstract-enum-ricky-carlson-way.html
Sun RFE:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6570766
To tidy up dave's code:
public class CodeableEnumHelper {
public static <E extends CodeableEnum> E getByCode(
String code, E[] values
) {
for (E e : values) {
if (e.getCode().equalsIgnoreCase(code)) {
return e;
}
}
return null;
}
}
public enum IMType implements CodableEnum {
...
public IMType getByCode(String code) {
return CodeableEnumHelper.getByCode(code, values());
}
}
Or more efficiently:
public class CodeableEnumHelper {
public static <E extends CodeableEnum> Map<String,E> mapByCode(
E[] values
) {
Map<String,E> map = new HashMap<String,E>();
for (E e : values) {
map.put(e.getCode().toLowerCase(Locale.ROOT), value) {
}
return map;
}
}
public enum IMType implements CodableEnum {
...
private static final Map<String,IMType> byCode =
CodeableEnumHelper.mapByCode(values());
public IMType getByCode(String code) {
return byCode.get(code.toLowerCase(Locale.ROOT));
}
}
I had a similar issue with a localization component that I wrote. My component is designed to access localized messages with enum constants that index into a resource bundle, not a hard problem.
I found that I was copying and pasting the same "template" enum code all over the place. My solution to avoid the duplication is a code generator that accepts an XML configuration file with the enum constant names and constructor args. The output is the Java source code with the "duplicated" behaviors.
Now, I maintain the configuration files and the generator, not all of the duplicated code. Everywhere I would have had enum source code, there is now an XML config file. My build scripts detect out-of-date generated files and invoke the code generator to create the enum code.
You can see this component here. The template that I was copying and pasting is factored out into an XSLT stylesheet. The code generator runs the stylesheet transformation. An input file is quite concise compared to the generated enum source code.
HTH,
Greg
Unfortunately, I don't think that there is a way to do this. Your best bet would pro ably be to give up in emums altogether and use conventional class extension and static members. Otherwise, get used to duplicating that code. Sorry.
Create a type-safe utility class which will load enums by code:
The interface comes down to:
public interface CodeableEnum {
String getCode();
}
The utility class is:
import java.lang.reflect.InvocationTargetException;
public class CodeableEnumUtils {
#SuppressWarnings("unchecked")
public static <T extends CodeableEnum> T getByCode(String code, Class<T> enumClass) throws IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
T[] allValues = (T[]) enumClass.getMethod("values", new Class[0]).invoke(null, new Object[0]);
for (T value : allValues) {
if (value.getCode().equals(code)) {
return value;
}
}
return null;
}
}
A test case demonstrating usage:
import junit.framework.TestCase;
public class CodeableEnumUtilsTest extends TestCase {
public void testWorks() throws Exception {
assertEquals(A.ONE, CodeableEnumUtils.getByCode("one", A.class));
assertEquals(null, CodeableEnumUtils.getByCode("blah", A.class));
}
enum A implements CodeableEnum {
ONE("one"), TWO("two"), THREE("three");
private String code;
private A(String code) {
this.code = code;
}
public String getCode() {
return code;
}
}
}
Now you are only duplicating the getCode() method and the getByCode() method is in one place. It might be nice to wrap all the exceptions in a single RuntimeException too :)
Here I have another solution:
interface EnumTypeIF {
String getValue();
EnumTypeIF fromValue(final String theValue);
EnumTypeIF[] getValues();
class FromValue {
private FromValue() {
}
public static EnumTypeIF valueOf(final String theValue, EnumTypeIF theEnumClass) {
for (EnumTypeIF c : theEnumClass.getValues()) {
if (c.getValue().equals(theValue)) {
return c;
}
}
throw new IllegalArgumentException(theValue);
}
}
The trick is that the inner class can be used to hold "global methods".
Worked pretty fine for me. OK, you have to implement 3 Methods, but those methods,
are just delegators.
It seems like you are actually implementing run time type information. Java provides this as a language feature.
I suggest you look up RTTI or reflection.
I don't think this is possible. However, you could use the enum's valueOf(String name) method if you were going to use the enum value's name as your code.
How about a static generic method? You could reuse it from within your enum's getByCode() methods or simply use it directly. I always user integer ids for my enums, so my getById() method only has do do this: return values()[id]. It's a lot faster and simpler.
If you really want inheritance, don't forget that you can implement the enum pattern yourself, like in the bad old Java 1.4 days.
About as close as I got to what you want was to create a template in IntelliJ that would 'implement' the generic code (using enum's valueOf(String name)). Not perfect but works quite well.
In your specific case, the getCode() / getByCode(String code) methods seems very closed (euphemistically speaking) to the behaviour of the toString() / valueOf(String value) methods provided by all enumeration. Why don't you want to use them?
Another solution would be not to put anything into the enum itself, and just provide a bi-directional map Enum <-> Code for each enum. You could e.g. use ImmutableBiMap from Google Collections for this.
That way there no duplicate code at all.
Example:
public enum MYENUM{
VAL1,VAL2,VAL3;
}
/** Map MYENUM to its ID */
public static final ImmutableBiMap<MYENUM, Integer> MYENUM_TO_ID =
new ImmutableBiMap.Builder<MYENUM, Integer>().
put(MYENUM.VAL1, 1).
put(MYENUM.VAL2, 2).
put(MYENUM.VAL3, 3).
build();
In my opinion, this would be the easiest way, without reflection and without adding any extra wrapper to your enum.
You create an interface that your enum implements:
public interface EnumWithId {
public int getId();
}
Then in a helper class you just create a method like this one:
public <T extends EnumWithId> T getById(Class<T> enumClass, int id) {
T[] values = enumClass.getEnumConstants();
if (values != null) {
for (T enumConst : values) {
if (enumConst.getId() == id) {
return enumConst;
}
}
}
return null;
}
This method could be then used like this:
MyUtil.getInstance().getById(MyEnum.class, myEnumId);
Related
Good day!
I have an interface which only implements one single method. I dont feel like making several class which all implement this one single method therefore I decided to use anonymous classes instead.
I use enums for certain static items, these enums have instances of my interface. However, when I try to make an anonymous class inside my enum constants my IDE (eclipse) literally tells me nothing (as if it is outside a code block).
My question is as follows: Can I use anonymous classes inside my enum constants?
If my text was unclear (Sorry im not english) please see the example below.
Code example
/**
* My Interface
*/
public interface IPotato {
public void eatPotato();
}
/**
* My enum class
*/
public enum PotatoEnum {
I_WANT_TO_EAT_POTATO(new IPotato() {
#Override
public void eatPotato() {
// Cant put code here.
} });
private IPotato _myAnonymousClass;
private PotatoEnum(IPotato anonymousClass){
this._myAnonymousClass = anonymousClass;
}
public IPotato getPotato(){
return _myAnonymousClass;
}
}
You could do that, it is a perfectly valid solution.
As a recommendation, make your enum implement your interface to make the code more readable:
public enum PotatoEnum implements IPotato{
I_WANT_TO_EAT_POTATO(){
#Override
public void eatPotato() {
// Cant put code here.
}},//more ENUMS ;
}
Simply yes
By doing this you are doing something like that:
I_WANT_TO_EAT_POTATO(An object of a virtual class that implments IPotato class);
same as :
I_WANT_TO_EAT_POTATO(Passing any parameter defined by constructor);
See Enum constants as an Inner Classes and you are passing them parameters of their construcors
You can to that. The reason of your mistake is that you have two public identifier (enum and interface) in single file . remove public from enum and it will work
public interface IPotato {
public void eatPotato();
}
enum PotatoEnum {
I_WANT_TO_EAT_POTATO(new IPotato() {
#Override
public void eatPotato() {
// Cant put code here.
}
});
private IPotato _myAnonymousClass;
private PotatoEnum(IPotato anonymousClass) {
this._myAnonymousClass = anonymousClass;
}
public IPotato getPotato() {
return _myAnonymousClass;
}
}
I'm new to java and Generics so please bear with me. I don't even know if this is possible. I've looked around and although there seem to be a few posts about this here and there I haven't found one that addresses my specific case clearly enough for me to understand what to do.
I basically have a static method in the parent class and would like it to return various types based on which child calls it. The trick here is that said child class that is returned also needs to be instantiated within the method.
Here's what I mean:
public class Parent {
public String value;
public Parent(String value)
{
this.value = value;
}
public static <T extends Parent> T load(DataStorage data, String key)
{
T inst = new ???(data.getValue(key));
return inst;
}
}
public class Child extends Parent {
}
Child child = Child.load(dataStore, "key"); // random dataStore instance
I'm not sure where to go from here. What do I use in place of ??? which should be whichever child (or Parent) runs the load() ? Do I need to also do something along the lines of Child.<Child>load()?
I'm open to alternative designs if you feel I'm mistaking in trying to do things like this. I don't like the idea of having to play around with Reflection in this situation (feels a little hacky)
Thanks in advance, I really appreciate it.
I guess what you want would be possible if Java didn't have type erasure and had 'constructor with parameter constraint' for generic types(like .net, but it has constraint for parameterless constructors only).
Maybe those two suits your needs:
If type of Child based on some selection criteria(e.g. an enumaration) I would go with a factory pattern like:
public class ParentFactory {
Parent create(SomeEnum t, DataStore dataStore, String key) {
switch (t) {
case SomeEnum.Child1Related:
return new Child1(dataStore.get(key));
...
}
}
}
But if creation is completely irrelevant(which is not in most cases), you can just define an init method in Parent and have initializer code there:
abstract class Parent {
String value;
Parent() {}
protected void init(String s) { this.value = s; }
static <T extends Parent> void initialize(DataStore data, String key, T t) {
t.init(data.getValue(key));
}
Child c = new Child();
Parent.init(dataStore, key, c);
You can make init method as private if you want to prohibit childs to intercept that call.
Honestly, I favor first one much more. Second one is a little ugly :)
It sounds like the thing you're looking for is a strongly-typed key object, which can associate a string with the corresponding child type. What I've done in the past is simply write a class for this key type.
Assuming your datastore looks something like this:
public interface DataStore {
DataItem get(String key);
}
And your various child classes look like this:
public final class Child1 extends Parent {
public Child1(DataItem dataItem) {
...
}
...
}
Your Key type could look like this:
/**
* Represents a way to construct an object from a {#link DataItem}.
*
* #param <T> the type of the object to construct.
*/
public final class Key<T extends Parent> {
private final String key;
// Assuming Java 8 Function. If you're using Java 7 or older,
// you can define your own Function interface similarly.
private final Function<DataItem, T> factoryFunction;
public Key(String key, Function<String, T> factoryFunction) {
this.key = checkNotNull(key);
this.factoryFunction = checkNotNull(factoryFunction);
}
public String key() {
return this.key;
}
public T constructFrom(DataItem dataItem) {
if (!key.equals(dataItem.getKey())) {
throw new IllegalStateException(
"DataItem is not valid for key " + key);
}
return factoryFunction.apply(dataItem);
}
}
Then you'll probably want a collection of well-known keys:
/** Well-known {#link Key} instances. */
public final class Keys {
private Keys() {} // static class
/** Key for {#link Child1}. */
public static final Key<Child1> FIRST_CHILD
= new Key<>("child1", Child1::new);
/** Key for {#link Child2}. */
public static final Key<Child2> SECOND_CHILD
= new Key<>("child2", Child2::new);
// etc.
}
Then you can define classes that work with these strongly-typed key instances:
public final class Loader {
private final DataStore dataStore;
public Loader(DataStore dataStore) {
this.dataStore = checkNotNull(dataStore);
}
public <T extends Parent> T load(Key<T> dataKey) {
return key.constructFrom(dataStore.get(dataKey.key()));
}
...
}
Note that this example still works even if you don't have Java 8 -- you'll just need to use an anonymous inline class to construct the child, rather than a lambda expression:
public static final Key<Child1> FIRST_CHILD =
new Key<Child1>("child1", new Function<DataItem, Child1>() {
#Override public Child1 apply(DataItem dataItem) {
return new Child1(dataItem);
}
});
You could of course use reflection for this part if you want, but manually writing the supplier functions will be faster. (Or, if you want the best of both worlds, you could use something like cglib's FastClass.) If you wanted to, you could also make the Key class abstract, so that you would subclass it and override a factory method rather than using a Function.
If you want to, you can merge the Loader type into your Parent class, but I wouldn't, since I think that would violate the Single Responsibility Principle -- typically you want the job of loading domain objects from storage to be separate from the domain objects themselves.
Hopefully that helps!
What I mean by type is something that would allow me to do the following.
public class AnyObject{
List<this.type> list;
}
I know the following dosen't work.
public class AnyObject{
List<this.getClass()> list;
}
So how would I create a lets say a list, for example sake, of type of whatever this is?
--------------- UPDATE ---------------
I apologize I don't think I was clear. I seem to be getting that there is no way to escape type erasure, but if there is still away to solve my problem I will explain it better. Disclosure, this is more of an Objectify question. Sorry I have come to see that now.
Here we go, clear as I can ...
For every entity I plan to persist, in GAE datastore using Objectiy, I would like to have a method to generate an Objectify Key<?> using the id and parent field. Lets call this method generateKey(). here is how it looks.
public Key<MyEntity> generateKey() {
Key<MyEntity> key = Key.create(this.parent, MyEntity.class, this.id);
return key;
}
The problem is I have to write this exact code, more or less, for every entity I create. Actually, there is other repeated code, but my point can be made with this piece of repeated code alone.
So I tried this. I created a class called MyProjectEntity and have all my entitys extend it. Then implemented a generateKey() method using generics.
public abstract class MyProjectEntity<T, Y> {
#Id Long id;
#Parent Key<T> parentKey;
public Key<Y> generateKey() {
Key<Y> key = Key.create(this.parentKey, this.getClass(), this.id);
return key;
}
}
Then I extended all my entity classes with this new class I created called MyProjectEntity. Like such ...
#Entity
public class MyEntity extends MyProjectEntity<MyEntityParent> {...}
Sounds good, now all my entity will have a generateKey() method, well this didn't quite work. Objectify yelled at me and said IllegalArgumentException, can not declare Key of type T.
Then I tried Key<Object>, Objectify was still unpleased, Objectify said Object is not a registered entity. Should I register Object!?!? and that kinda loses the whole point to a typed key that Objectify offers.
Is there a good solution. Thanks!
-- UPDATE 2 --
Since someone pointed out Key.create(myEntity) I should point my full use ...
/**********************************************************************************************************************
* Constructors END & Identification and Relationship Methods BEGIN
**********************************************************************************************************************/
#ApiSerializationProperty(name = "id")
public String getWebSafeKey() {
String webSafeKey = getKey().getString();
return webSafeKey;
}
public void setWebSafeKey(String webSafeKey) throws BadRequestException {
try {
Key<MyEntity> key = Key.create(webSafeKey);
setKey(key);
} catch (IllegalArgumentException illegalArgumentException) {
throw new BadRequestException(ErrorMessage.INVALID_ID);
}
}
#ApiSerializationProperty(name = "parentId")
public String getParentWebSafeKey() {
String webSafeKey = parent.getString();
return webSafeKey;
}
public void setParentWebSafeKey(String parentWebSafeKey) throws BadRequestException {
if (id == null) {
try {
parent = Key.create(parentWebSafeKey);
} catch (IllegalArgumentException illegalArgumentException) {
throw new BadRequestException(ErrorMessage.invalidParentId("Property"));
}
} else {
/* Do nothing. Only set parent here if setWebSafeKey is never called, such as during a create. */
}
}
#ApiSerializationProperty(ignored = AnnotationBoolean.TRUE)
public Key<MyEntity> getParentKey() {
return parent;
}
public void setParentKey(Key<MyEntity> parentKey) {
this.parent = parentKey;
}
#ApiSerializationProperty(ignored = AnnotationBoolean.TRUE)
public Key<MyEntity> getKey() {
Key<MyEntity> key = Key.create(parent, MyEntity.class, id);
return key;
}
public void setKey(Key<MyEntity> key) {
id = key.getId();
parent = key.getParent();
}
public boolean webSafeKeyEquals(String webSafeKey) {
boolean equals;
if (id !=null & parent !=null) {
equals = getWebSafeKey().equals(webSafeKey);
} else {
equals = false;
}
return equals;
}
/**********************************************************************************************************************
* Identification Methods END & Other Getters and Setters BEGIN
**********************************************************************************************************************/
All this has to be inserted for every entity I create with MyEntity replaced for the actual entity name. It's not just typing. This code doesn't belong in the entity class, but rather in some abstract parent. If I could have only code unique to a particular entity in the class, my model would be cleaner, and easier to expand. Thanks again.
This would not make sense. Consider: you would never know what the type of list is. Suppose that list is used in some method of some class, it could always be that this is an instance of a subclass. So the parameter of List in the type of list can never be assumed in any code. If it can never be known, then what is the point of it? You would just use List<?>.
Generics is a purely compile-time thing. Therefore, it is meaningless to depend on the runtime class of something.
I suggest that you have
public class AnyObject<T> {
List<T> list;
}
and any class Foo which wants to have list be a List<Foo>, for example, should just implement or inherit from AnyObject<Foo>.
This does not make sense List<this.getClass()> list; as the type parameters are compile time thing in java. This information is erased at runtime.
Without being familiar with Objectify, just generics, the thing I see is that Key.create is supposed to itself take a generic argument <T> for the type of returned Key. So you would be supposed to do the following when you call the method in the superclass:
Key<Y> key = Key.<Y>create(this.parentKey, this.getClass(), this.id);
You may only simply have to do that to fix the error (and should be doing it anyway). Otherwise Key.create will try to instantiate a new Key<Y> and although it is more or less valid to not declare a type argument when a method asks for one, apparently Key.create may not like that.
I think you should also take another look at your Ts and Ys because it appears you are mixing them. Right now you are handing Key.create a Key<T> as a parameter but wanting to return a Key<Y>. Also if you declare your class as having <T, Y> it should be illegal to extend it with only <MyEntityParent>.
Looking at your code I think what you are trying to do is create Key of the same class as the method you are calling it from. IE class generateKey in MyEntity should return a Key<MyEntity>. I think the proper way to do this would be like so (which is valid):
public abstract class MyProjectEntity<T, K> {
Long id;
Key<K> parentKey;
public Key<K> generateKey() {
return Key.<K>create(parentKey, this.getClass(), id);
}
}
public class MyEntity extends MyEntityParent<MyEntityParent, MyEntity> {
/*
* K is now MyEntity and parentKey is a Key<MyEntity>
* generateKey now does the following:
*
* public Key<MyEntity> generateKey() {
* return Key.<MyEntity>create(parentKey, MyEntity.class, id);
* }
*
*/
}
It just seems like your example that doesn't work is giving the error because you aren't declaring the types properly. But it is hard to tell because it is unclear what your T and Y are supposed to be. You only show one of the types being declared and at least in your generateKey method you are handing Key.create a Key<T> but wanting to return a Key<Y>.
Or perhaps you should take a look at Registering Entities in the Objectify API. IE it seems you might be supposed to do something like this and that is a possible reason you are getting the error:
static {
ObjectifyService.register(MyEntityParent.class);
}
But anyway in the world of Java generics you really ought to be able to do something like this without any gymnastics unless something else is going on. The nature of erasure is that you can't find out the type at runtime but the type is essentially "known" because all instances of T are replaced with the argument type.
public abstract class MyProjectEntity<T> {
Key<T> parentKey;
}
becomes
public class MyEntity extends MyProjectEntity<MyEntityParent> {
Key<MyEntityParent> parentKey;
}
You can't find out whether or not parentKey is of Type <MyEntityParent> but it is of that type. You can obviously see this with something like a java.util.List where if you do the following:
List<Double> doubleList = new ArrayList<Double>(0);
doubleList.add("a string");
You will get the following if you ignore the compiler errors and try to run the program anyway:
Uncompilable source code - Erroneous sym type: java.util.ArrayList.add
java.lang.RuntimeException: Uncompilable source code - Erroneous sym type: java.util.ArrayList.add
Because the list does "only hold" instances of Double. This situation could be compared to an anonymous class where that instance of an ArrayList's add method now officially takes a Double as an argument. It is uncompilable because I just tried to do this:
public void add(Double element) {
// add the element to the array
}
list.add("a string");
Which is obviously illegal. This ArrayList's underlying array is still an Object[] but the methods will be changed to reflect the type and safely make sure the array only holds Double elements at runtime.
So I would recommend taking a look at the things I mentioned because it appears that there's more than one problem unless you've omitted relevant code.
I think I understand your problem and here is how you could do it. The trick is to pass the subclass as a generic parameter of the parent class:
class Parent<T> {
T doStuff() {
T res = null;
// res = ..... this.getClass() is ok...
return res;
}
}
public class SelfGerenic extends Parent<SelfGerenic> {
}
public class OtherSubClass extends Parent<OtherSubClass> {
}
If I got you right, you're looking for something like this:
public class Test {
private int id;
public Key<Test> getKey() {
return createKey(id, this.getClass());
}
public static <T> Key<T> createKey(int id, Class<? extends T> clazz) {
return new Key<T>(clazz, id);
}
private static class Key<T> {
private final Class<? extends T> clazz;
private final int id;
private Key(Class<? extends T> clazz, int id) {
this.clazz = clazz;
this.id = id;
}
private int getId() {
return id;
}
private Class<? extends T> getClazz() {
return clazz;
}
}
public int getId() {
return id;
}
}
It is not possible to replace Test here: public Key<Test> getKey() {!
This is because getKey() always returns Key. It can not return Test.
So basically no, there is no way to change this behaviour. Also there is no way to get the generic type of the "current" class. This is some kind of limit of the java generics :P
You could remove the generics here, so you do not have to implement getKey() every time.
public class Test {
private int id;
public Key getKey() {
return createKey(id, this.getClass());
}
public static Key createKey(int id, Class clazz) {
return new Key(clazz, id);
}
private static class Key {
private final Class clazz;
private final int id;
private Key(Class clazz, int id) {
this.clazz = clazz;
this.id = id;
}
private int getId() {
return id;
}
private Class getClazz() {
return clazz;
}
}
public int getId() {
return id;
}
}
I have;
public enum Detailed {
PASSED, INPROCESS, ERROR1, ERROR2, ERROR3;
}
and need to convert it to the following;
public enum Simple {
DONE, RUNNING, ERROR;
}
So first PASSED->DONE and INPROCESS->RUNNING, but all errors should be: ERROR. Obviously it is possible to write cases for all values, but there may be a better solution?
Personally I would just create a Map<Detailed, Simple> and do it explicitly - or even use a switch statement, potentially.
Another alternative would be to pass the mapping into the constructor - you could only do it one way round, of course:
public enum Detailed {
PASSED(Simple.DONE),
INPROCESS(Simple.RUNNING),
ERROR1(Simple.ERROR),
ERROR2(Simple.ERROR),
ERROR3(Simple.ERROR);
private final Simple simple;
private Detailed(Simple simple) {
this.simple = simple;
}
public Simple toSimple() {
return simple;
}
}
(I find this simpler than Ted's approach of using polymorphism, as we're not really trying to provide different behaviour - just a different simple mapping.)
While you could potentially do something cunning with the ordinal value, it would be much less obvious, and take more code - I don't think there'd be any benefit.
One way is to define a method asSimple() in your Detailed enum:
public enum Detailed {
PASSED {
#Override
Simple asSimple() {
return DONE;
}
},
INPROCESS {
#Override
Simple asSimple() {
return RUNNING;
}
},
ERROR1,
ERROR2,
ERROR3;
public Simple asSimple() {
return Simple.ERROR; // default mapping
}
}
You can then simply call the method when you want to do the mapping:
Detailed code = . . .
Simple simpleCode = code.asSimple();
It has the advantage of putting the knowledge of the mapping with the Detailed enum (where perhaps it belongs). It has the disadvantage of having knowledge of Simple mixed in with the code for Detailed. This may or may not be a bad thing, depending on your system architecture.
Use EnumMap
I decouple my external xml interface from my internal domain model by implementing a transformation service. This includes mapping enums from jaxb generated code to domain model enums.
Using a static EnumMap encapsulates the concern of transformation within the class responsible for transformation. Its cohesive.
#Service
public class XmlTransformer {
private static final Map<demo.xml.Sense, Constraint.Sense> xmlSenseToSense;
static {
xmlSenseToSense = new EnumMap<demo.xml.Sense, Constraint.Sense> (
demo.xml.Sense.class);
xmlSenseToSense.put(demo.xml.planningInterval.Sense.EQUALS,
Constraint.Sense.EQUALS);
xmlSenseToSense.put(demo.xml.planningInterval.Sense.GREATER_THAN_OR_EQUALS,
Constraint.Sense.GREATER_THAN_OR_EQUALS);
xmlSenseToSense.put(demo.xml.planningInterval.Sense.LESS_THAN_OR_EQUALS,
Constraint.Sense.LESS_THAN_OR_EQUALS);
}
...
}
Guava's Enums.getIfPresent() on Enum.name()
Our case was a particular specialization of this one. We do have two Enum: one we use in the application and another one we use in the core library. The core library is used by a handful of applications, by different teams. Each application views a subset of the whole functionality. The whole functionality is configured with the enums in order to switch on and off, throttle up or down, select strategies, etc.
So we ended up with:
one enum for the library, containing all the possible configurations, visible from the applications and also some library-specific
one enum for each application, containing the literals corresponding to what the application can see/touch in the library, and some application-specific
Then as we pass data down to the library, we adapt all data and also those configurations. We own all enums, so we can choose to call the same configuration with the same literal in different enums.
Enum LibraryConfig {
FUNCTION_ONE,
FUNCTION_TWO,
FUNCTION_THREE,
FUNCTION_FOUR;
}
Enum Aplication1Config {
FUNCTION_ONE,
FUNCTION_TWO,
FUNCTION_THREE,
APPL1_FUNCTION_ONE,
APPL1_FUNCTION_TWO;
}
Enum Aplication2Config {
FUNCTION_ONE,
FUNCTION_TWO,
FUNCTION_FOUR;
APPL2_FUNCTION_ONE;
}
When we need to convert from one type to another (app --> lib or lib --> app) we use the getIfPresent() method from com.google.common.base.Enums in this way:
Aplication1Config config1App1 = FUNCTION_TWO;
LibraryConfig configLib = Enums.getIfPresent(LibraryConfig.class, config1App1.name()).orNull();
We check configLib for null value to see if there was successful conversion. This last step we use because of the APPX_FUNCTION_YYY, which are application-specific, and for the conversion on the direction lib --> app, not to pass configuration values library-specific (FUNCTION_FOUR in the example).
maven's dependency management:
Just in case anyone needs it:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>
Home grown version:
You can make your own conversion using the Enum methods, but you have to take care of the exception to detect when the conversion did not succeed:
try {
Aplication1Config config1App1 = FUNCTION_TWO;
LibraryConfig configLib = LibraryConfig.valueOf(config1App1.name());
} catch (IllegalArgumentException iae) {
// if the conversion did not succeed
}
Ted's answer is very Javaly, but the expression
passed == PASSED ? DONE : ERROR
would do the job, too.
To me that sounds more like a conceptual problem than a programming problem. Why don't you just remove the "Simple" enum type and use the other one instead in all places in the program?
Just to make that more clear with another example: Would you really try to define an enum type for the work days in a week (Monday to Friday) and another enum for all days of a week (Monday to Sunday)?
Here is the simple enum mapper with test:
-- IMPLEMENTATION
-- ENUMS
public enum FirstEnum {
A(0), B(1);
private final int value;
private FirstEnum(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
public enum SecondEnum {
C(0), D(1);
private final int valueId;
private SecondEnum(int valueId) {
this.valueId = valueId;
}
public int getValueId() {
return valueId;
}
}
--MAPPER
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.Validate;
import com.google.common.collect.Sets;
public class EnumPropertyMapping {
private final Map<?, ?> firstMap;
private final Map<?, ?> secondMap;
private final Class<?> firstType;
private final Class<?> secondType;
private EnumPropertyMapping(
Map<?, ?> firstMap, Map<?, ?> secondMap, Class<?> firstType, Class<?> secondType) {
this.firstMap = firstMap;
this.secondMap = secondMap;
this.firstType = firstType;
this.secondType = secondType;
}
public static Builder builder() {
return new Builder();
}
#SuppressWarnings("unchecked")
public <R> R getCorrespondingEnum(Object mappedEnum) {
Validate.notNull(mappedEnum, "Enum must not be NULL");
Validate.isInstanceOf(Enum.class, mappedEnum, "Parameter must be an Enum");
if (firstType.equals(mappedEnum.getClass())) {
return (R) firstMap.get(mappedEnum);
}
if (secondType.equals(mappedEnum.getClass())) {
return (R) secondMap.get(mappedEnum);
}
throw new IllegalArgumentException("Didn't found mapping for enum value: " + mappedEnum);
}
public static class Builder {
private final Map<Object, Object> firstEnumMap = new HashMap<>();
private final Map<Object, Object> secondEnumMap = new HashMap<>();
private Class<?> firstEnumType;
private Class<?> secondEnumType;
public <T extends Enum<T>> Builder addFirst(Class<T> enumType, String propertyName) {
firstEnumType = enumType;
initMap(firstEnumMap, enumType.getEnumConstants(), propertyName);
return this;
}
public <T extends Enum<T>> Builder addSecond(Class<T> enumType, String propertyName) {
secondEnumType = enumType;
initMap(secondEnumMap, enumType.getEnumConstants(), propertyName);
return this;
}
private void initMap(Map<Object, Object> enumMap, Object[] enumConstants, String propertyName) {
try {
for (Object constant : enumConstants) {
enumMap.put(PropertyUtils.getProperty(constant, propertyName), constant);
}
} catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException ex) {
throw new IllegalStateException(ex);
}
}
public EnumPropertyMapping mapEnums() {
Validate.isTrue(firstEnumMap.size() == secondEnumMap.size());
Validate.isTrue(Sets.difference(firstEnumMap.keySet(), secondEnumMap.keySet()).isEmpty());
Map<Object, Object> mapA = new HashMap<>();
Map<Object, Object> mapB = new HashMap<>();
for (Map.Entry<Object, Object> obj : firstEnumMap.entrySet()) {
Object secondMapVal = secondEnumMap.get(obj.getKey());
mapA.put(obj.getValue(), secondMapVal);
mapB.put(secondMapVal, obj.getValue());
}
return new EnumPropertyMapping(mapA, mapB, firstEnumType, secondEnumType);
}
}
}
-- TEST
import org.junit.Test;
import com.bondarenko.common.utils.lang.enums.FirstEnum;
import com.bondarenko.common.utils.lang.enums.SecondEnum;
import static junit.framework.TestCase.assertEquals;
public class EnumPropertyMappingTest {
#Test
public void testGetMappedEnum() {
EnumPropertyMapping mapping = EnumPropertyMapping.builder()
.addSecond(SecondEnum.class, "valueId")
.addFirst(FirstEnum.class, "value")
.mapEnums();
assertEquals(SecondEnum.D, mapping.getCorrespondingEnum(FirstEnum.B));
assertEquals(FirstEnum.A, mapping.getCorrespondingEnum(SecondEnum.C));
}
}
When you use the Visitor pattern and you need to get a variable inside visitor method, how to you proceed ?
I see two approaches. The first one uses anonymous class :
// need a wrapper to get the result (which is just a String)
final StringBuild result = new StringBuilder();
final String concat = "Hello ";
myObject.accept(new MyVisitor() {
#Override
public void visit(ClassA o)
{
// this concatenation is expected here because I've simplified the example
// normally, the concat var is a complex object (like hashtable)
// used to create the result variable
// (I know that concatenation using StringBuilder is ugly, but this is an example !)
result.append(concat + "A");
}
#Override
public void visit(ClassB o)
{
result.append(concat + "B");
}
});
System.out.println(result.toString());
Pros & Cons :
Pros : you do not need to create a class file for this little behavior
Cons : I don't like the "final" keyword in this case : the anonymous class is less readable because it calls external variables and you need to use a wrapper to get the requested value (because with the keyword final, you can't reassign the variable)
Another way to do it is to do an external visitor class :
public class MyVisitor
{
private String result;
private String concat;
public MyVisitor(String concat)
{
this.concat = concat;
}
#Override
public void visit(ClassA o)
{
result = concat + "A";
}
#Override
public void visit(ClassB o)
{
result = concat + "B";
}
public String getResult()
{
return result;
}
}
MyVisitor visitor = new MyVisitor("Hello ");
myObject.accept(visitor);
System.out.println(visitor.getResult());
Pros & Cons :
Pros : all variables are defined in a clean scope, you don't need a wrapper to encapsulate the requested variable
Cons : need an external file, the getResult() method must be call after the accept method, this is quite ugly because you need to know the function call order to correctly use the visitor
You, what's your approach in this case ? Preferred method ? another idea ?
Well, both approaches are valid and imo, it really depends on whether you would like to reuse the code or not. By the way, your last 'Con' point is not totally valid since you do not need an 'external file' to declare a class. It might very well be an inner class...
That said, the way I use Visitors is like this:
public interface IVisitor<T extends Object> {
public T visit(ClassA element) throws VisitorException;
public T visit(ClassB element) throws VisitorException;
}
public interface IVisitable {
public <T extends Object> T accept(final IVisitor<T> visitor) throws VisitorException;
}
public class MyVisitor implements IVisitor<String> {
private String concat;
public MyVisitor(String concat) {
this.concat = concat;
}
public String visit(ClassA classA) throws VisitorException {
return this.concat + "A";
}
public String visit(ClassB classB) throws VisitorException {
return this.concat + "B";
}
}
public class ClassA implements IVisitable {
public <T> T accept(final IVisitor<T> visitor) throws VisitorException {
return visitor.visit(this);
}
}
public class ClassB implements IVisitable {
public <T> T accept(final IVisitor<T> visitor) throws VisitorException {
return visitor.visit(this);
}
}
// no return value needed?
public class MyOtherVisitor implements IVisitor<Void> {
public Void visit(ClassA classA) throws VisitorException {
return null;
}
public Void visit(ClassB classB) throws VisitorException {
return null;
}
}
That way, the visited objects are ignorant of what the visitor wants to do with them, yet they do return whatever the visitor wants to return. Your visitor can even 'fail' by throwing an exception.
I wrote the first version of this a few years ago and so far, it has worked for me in every case.
Disclaimer: I just hacked this together, quality (or even compilation) not guaranteed. But you get the idea... :)
I do not see an interface being implemented in your second example, but I believe it is there. I would add to your interface (or make a sub interface) that has a getResult() method on it.
That would help both example 1 and 2. You would not need a wrapper in 1, because you can define the getResult() method to return the result you want. In example 2, because getResult() is a part of your interface, there is no function that you 'need to know'.
My preference would be to create a new class, unless each variation of the class is only going to be used once. In which case I would inline it anonymously.
From the perspective of a cleaner design, the second approach is preferrable for the same exact reasons you've already stated.
In a normal TDD cycle I would start off with an anonymous class and refactored it out a bit later. However, if the visitor would only be needed in that one place and its complexity would match that of what you've provided in the example (i.e. not complex), I would have left it hanging and refactor to a separate class later if needed (e.g. another use case appeared, complexity of the visitor/surrounding class increased).
I would recommend using the second approach. Having the visitor in its full fledged class also serves the purpose of documentation and clean code. I do not agree with the cons that you have mentioned with the approach. Say you have an arraylist, and you don't add any element to it and do a get, surely you will get a null but that doesn't mean that it is necessarily wrong.
One of the points of the visitor pattern is to allow for multiple visitor types. If you create an anonymous class, you are kind of breaking the pattern.
You should change your accept method to be
public void accept(Visitor visitor) {
visitor.visit(this);
}
Since you pass this into the visitor, this being the object that is visited, the visitor can access the object's property according to the standard access rules.