How to get value by Java enum class name and field name?
The sample code is as follows, but I don't know how to pass the enum class as a parameter.
public enum ErrorCodes1{
OK(0),
NOT_EXIST_USER(1),
FAIL_TO_SEND_MAIL(2),
...
}
public enum ErrorCodes2{
OK(0),
NOT_EXIST_USER(1),
FAIL_TO_SEND_MESSA(2),
...
}
public void foo1()
{
foo2(ErrorCodes1.class, "NOT_EXIST_USER");
foo2(ErrorCodes2.class, "NOT_EXIST_USER");
}
public void foo2(Enum EnumClass, String EnumText)
{
int code = xxxx; //I want to get code(1) via EnumText and EnumClass, but I don't know how to do it.
}
You can use the type Class as a param of your function foo2
public static void foo2(Class<?> enumClass, String enumText) {
int code = -1; // I want to get code(1) via EnumText and EnumClass, but I don't know how to do
// it.
switch (enumClass.getCanonicalName()) {
case "ErrorCodes2": {
ErrorCodes1 errorCode = ErrorCodes1.valueOf(enumText);
code = errorCode.ordinal();
}
case "ErrorCodes1": {
ErrorCodes1 errorCode = ErrorCodes1.valueOf(enumText);
code = errorCode.ordinal();
}
}
System.out.println(code);
}
After that, you can use valueOf to instantiate your enum from a string that contains the enum value. I don't think this is the best solution but its works.
PS: The param of a function begins with a lowercase letter.
Option A, simple: check for the type explicitly.
int code;
if (enumClass instanceof ErrorCodes1) {
code = ((ErrorCodes1) enumClass).valueOf(enumText).ordinal();
} else if (enumClass instanceof ErrorCodes2)
// repeat
This implies you can pass an error object itself, not necessarily its class. Unfortunately, switch does not work with Class type.
Option B, probably overkill: use Reflection.
public void foo2(Class<?> enumClass, String enumText) {
try{
Object resultingEnum = enumClass.getMethod("valueOf", String.class).invoke(null, enumText);
int code = (Integer) resultingEnum.getClass().getMethod("ordinal").invoke(resultingEnum);
// ...
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e ) {
// ignore: enumClass is not an expected class
}
}
This will suit you in case you have an unlimited number of ErrorCode enums, though it is a barely plausible situation.
Related
In my program, the user needs to input what type of players the game will have. The players are "human", "good" (for a good AI), "bad" (for a bad AI) and "random" (for a random AI). Each of these players have their own class that extend one abstract class called PlayerType.
My struggle is mapping a String to the object so I can A) create a new object using the String as sort of a key and B) get the related String from an object of its subclass
Ultimately, I just want the implicit String to only appear once in the code so I can change it later if needed without refactoring.
I've tried using just a plain HashMap, but that seems clunky with searching the keys via the values. Also, I'm guessing that I'll have to use the getInstance() method of Class, which is a little less clunky, which is okay if it's the only way.
What I would do is create an enum which essentially functions as a factory for the given type.
public enum PlayerTypes {
GOOD {
#Override
protected PlayerType newPlayer() {
return new GoodPlayer();
}
},
BAD {
#Override
protected PlayerType newPlayer() {
return new BadPlayer();
}
},
RANDOM {
#Override
protected PlayerType newPlayer() {
return new RandomPlayer();
}
};
protected abstract PlayerType newPlayer();
public static PlayerType create(String input) {
for(PlayerTypes player : PlayerTypes.values()) {
if(player.name().equalsIgnoreCase(input)) {
return player.newPlayer();
}
}
throw new IllegalArgumentException("Invalid player type [" + input + "]");
}
)
Because then you can just call it like so:
String input = getInput();
PlayerTypes.create(input);
Of course, you'll get an IllegalArgumentException which you should probably handle by trying to get the input again.
EDIT: Apparently in this particular case, you can replace that loop with just merely
return PlayerTypes.valueOf(input).newPlayer();
And it'll do the same thing. I tend to match for additional constructor parameters in the enum, so I didn't think of using valueOf(), but it's definitely cleaner.
EDIT2: Only way to get that information back is to define an abstract method in your PlayerType class that returns the PlayerTypes enum for that given type.
public class PlayerType {
public abstract PlayerTypes getType();
}
public class GoodPlayer extends PlayerType {
#Override
public PlayerTypes getType() {
return PlayerTypes.GOOD;
}
}
I like the answer provided by Epic but I don't find maps to be clunky. So it's possible to keep a map and get the constructor call directly.
Map<String, Supplier<PlayerType> map = new HashMap<>();
map.put("human", Human::new);
Human h = map.get("human").get();
The two main options I can think of:
Using Class.newInstance(), as you mentioned (not sure if you had this exact way in mind):
// Set up your map
Map<String, Class> classes = new HashMap<String, Class>();
classes.put("int", Integer.class);
classes.put("string", String.class);
// Get your data
Object s = classes.get("string").newInstance();
You could use Class.getDeclaredConstructor.newInstance if you want to use a constructor with arguments (example).
Another option is using switch:
Object getObject(String identifier) {
switch (identifier) {
case "string": return new String();
case "int": return new Integer(4);
}
return null; // or throw an exception or return a default object
}
One potential solution:
public class ForFunFactory {
private ForFunFactory() {
}
public static AThing getTheAppropriateThing(final String thingIdentifier) {
switch (thingIdentifier) {
case ThingImplApple.id:
return new ThingImplApple();
case ThingImplBanana.id:
return new ThingImplBanana();
default:
throw new RuntimeException("AThing with identifier "
+ thingIdentifier + " not found.");
}
}
}
public interface AThing {
void doStuff();
}
class ThingImplApple implements AThing {
static final String id = "Apple";
#Override
public void doStuff() {
System.out.println("I'm an Apple.");
}
}
class ThingImplBanana implements AThing {
static final String id = "Banana";
#Override
public void doStuff() {
System.out.println("I'm a Banana.");
}
}
I have a Java (for Android) code similar to this one:
enum MyEnum {
A, B, C
}
String f(MyEnum e) {
if (e == null) {
return null;
}
switch(e) {
case A: return "AA";
case B: return "BB";
case C: return "CC";
default: throw new IllegalStateException("invalid enum");
}
}
and I got the exception in the default clause thrown once! Can somebody explain if this is theoretically possible and how?
For example in C++ you can have an enum variable which value is non of the declared enum values, but I guess in Java you cannot do that, correct me if I am wrong.
I dont see how this could fail, but i would propose refactoring your enum to this:
enum MyEnum {
A("AA"),
B("BB"),
C("CC");
private final String value;
public MyEnum(String value){
this.value = value;
}
public String f(){
return value;
}
}
now you can still do the same operations, but it 100% safe to add new enums
public void foo(MyEnum enum){
System.out.println(enum.f());
}
Just playing and came up with a sweet way to add functionality to enums in Java Enum toString() method with this.
Some further tinkering allowed me to nearly also add a tidy (i.e. not throwing an exception) reverse look-up but there's a problem. It's reporting:
error: valueOf(String) in X cannot implement valueOf(String) in HasValue
public enum X implements PoliteEnum, ReverseLookup {
overriding method is static
Is there a way?
The aim here is to silently add (via an interface implementation with a default method like I added politeName in the linked answer) a lookup method that does the valueOf function without throwing an exception. Is it possible? It is clearly now possible to extend enum - one of my major problems with Java until now.
Here's my failed attempt:
public interface HasName {
public String name();
}
public interface PoliteEnum extends HasName {
default String politeName() {
return name().replace("_", " ");
}
}
public interface Lookup<P, Q> {
public Q lookup(P p);
}
public interface HasValue {
HasValue valueOf(String name);
}
public interface ReverseLookup extends HasValue, Lookup<String, HasValue> {
#Override
default HasValue lookup(String from) {
try {
return valueOf(from);
} catch (IllegalArgumentException e) {
return null;
}
}
}
public enum X implements PoliteEnum/* NOT ALLOWED :( , ReverseLookup*/ {
A_For_Ism, B_For_Mutton, C_Forth_Highlanders;
}
public void test() {
// Test the politeName
for (X x : X.values()) {
System.out.println(x.politeName());
}
// ToDo: Test lookup
}
You are over-complicating your design. If you are willing to accept that you can invoke a default method on an instance only, there entire code may look like this:
interface ReverseLookupSupport<E extends Enum<E>> {
Class<E> getDeclaringClass();
default E lookup(String name) {
try {
return Enum.valueOf(getDeclaringClass(), name);
} catch(IllegalArgumentException ex) { return null; }
}
}
enum Test implements ReverseLookupSupport<Test> {
FOO, BAR
}
You can test it with:
Test foo=Test.FOO;
Test bar=foo.lookup("BAR"), baz=foo.lookup("BAZ");
System.out.println(bar+" "+baz);
An non-throwing/catching alternative would be:
interface ReverseLookupSupport<E extends Enum<E>> {
Class<E> getDeclaringClass();
default Optional<E> lookup(String name) {
return Stream.of(getDeclaringClass().getEnumConstants())
.filter(e->e.name().equals(name)).findFirst();
}
to use like:
Test foo=Test.FOO;
Test bar=foo.lookup("BAR").orElse(null), baz=foo.lookup("BAZ").orElse(null);
System.out.println(bar+" "+baz);
Here, there's basically two points. Specifically the reason it doesn't compile is 8.4.8.1:
It is a compile-time error if an instance method overrides a static method.
In other words, an enum can't implement HasValue because of the name clash.
Then there's the more general issue we have which is that static methods just cannot be 'overridden'. Since valueOf is a static method inserted by the compiler on the Enum-derived class itself, there's no way to change it. We also can't use interfaces to solve it since they do not have static methods.
In this specific case it's a place where composition can make this kind of thing less repetetive, for example:
public class ValueOfHelper<E extends Enum<E>> {
private final Map<String, E> map = new HashMap<String, E>();
public ValueOfHelper(Class<E> cls) {
for(E e : EnumSet.allOf(cls))
map.put(e.name(), e);
}
public E valueOfOrNull(String name) {
return map.get(name);
}
}
public enum Composed {
A, B, C;
private static final ValueOfHelper<Composed> HELPER = (
new ValueOfHelper<Composed>(Composed.class)
);
public static Composed valueOfOrNull(String name) {
return HELPER.valueOfOrNull(name);
}
}
(Plus, I'd recommend that over catching the exception anyway.)
I realize "you can't do it" is not really a desirable answer but I don't see a way around it due to the static aspect.
The case is the same as you can not create default toString() in interface. The enum already contains signature for static valueOf(String) method therefore you can not override it.
The enum are compile time constant and because of that it really doubtful that they will be extensible someday.
If you want to get the constant via name you can use this:
public static <E extends Enum<E>> Optional<E> valueFor(Class<E> type, String name) {
return Arrays.stream(type.getEnumConstants()).filter( x -> x.name().equals(name)).findFirst();
}
I think I have an answer - it's hacky and uses reflection but seems to fit the brief - i.e. reverse lookup without methods in the enum and without throwing exception.
public interface HasName {
public String name();
}
public interface PoliteEnum extends HasName {
default String politeName() {
return name().replace("_", " ");
}
}
public interface Lookup<P, Q> {
public Q lookup(P p);
}
public interface ReverseLookup<T extends Enum<T>> extends Lookup<String, T> {
#Override
default T lookup(String s) {
return (T) useMap(this, s);
}
}
// Probably do somethiong better than this in the final version.
static final Map<String, Enum> theMap = new HashMap<>();
static Enum useMap(Object o, String s) {
if (theMap.isEmpty()) {
try {
// Yukk!!
Enum it = (Enum)o;
Class c = it.getDeclaringClass();
// Reflect to call the static method.
Method method = c.getMethod("values");
// Yukk!!
Enum[] enums = (Enum[])method.invoke(null);
// Walk the enums.
for ( Enum e : enums) {
theMap.put(e.name(), e);
}
} catch (Exception ex) {
// Ewwww
}
}
return theMap.get(s);
}
public enum X implements PoliteEnum, ReverseLookup<X> {
A_For_Ism,
B_For_Mutton,
C_Forth_Highlanders;
}
public void test() {
for (X x : X.values()) {
System.out.println(x.politeName());
}
for (X x : X.values()) {
System.out.println(x.lookup(x.name()));
}
}
prints
A For Ism
B For Mutton
C Forth Highlanders
A_For_Ism
B_For_Mutton
C_Forth_Highlanders
Added
Inspired by #Holger - this is what I feel is most like what I was looking for:
public interface ReverseLookup<E extends Enum<E>> extends Lookup<String, E> {
// Map of all classes that have lookups.
Map<Class, Map<String, Enum>> lookups = new ConcurrentHashMap<>();
// What I need from the Enum.
Class<E> getDeclaringClass();
#Override
default E lookup(String name) throws InterruptedException, ExecutionException {
// What class.
Class<E> c = getDeclaringClass();
// Get the map.
final Map<String, Enum> lookup = lookups.computeIfAbsent(c,
k -> Stream.of(c.getEnumConstants())
// Roll each enum into the lookup.
.collect(Collectors.toMap(Enum::name, Function.identity())));
// Look it up.
return c.cast(lookup.get(name));
}
}
// Use the above interfaces to add to the enum.
public enum X implements PoliteName, ReverseLookup<X> {
A_For_Ism,
B_For_Mutton,
C_Forth_Highlanders;
}
I have multiple classes that all inherit from the same Block class, and want to instantiate one of them based on a variable value.
Here is what I did for now:
public enum MapCases {
FLOOR (Floor.class), // These are all subclass of my Block class
WALL (Wall.class),
ROCK (Rock.class);
private Class<?> blockType;
MapCases (Class<?> pointing) {
this.block = pointing;
}
public Class<?> getType () {
return this.blockType;
}
};
Then later, I try to instantiate them given some data:
private Block[] blocks; // Array of the mother type
...
blocks = new Block[data.length]; // data is an int array
for (int i = 0; i < data.length; i++) {
int choice = data[i];
Class<?> blockType = MapCases.values()[choice].getType();
blocks[i] = new blockType(); // blockType is of unresolved type
}
But Eclipse shows me an error saying it can't resolve blockType to a type (which seems logical given the fact that java don't know yet the type).
How could I achieve what I am trying to do?
You shouldn't need reflection to do this. Consider this instead:
public enum MapCases {
FLOOR {
#Override
public Block makeBlock() {
return new Floor();
}
},
WALL {
#Override
public Block makeBlock() {
return new Wall();
}
},
ROCK {
#Override
public Block makeBlock() {
return new Rock();
}
};
public abstract Block makeBlock();
}
In this case, the enum itself acts as the factory instead of the Class token it was holding.
Note that if you did want to stick with the Class token, it should be typed as Class<? extends Block>, as Elliott Frisch points out in the comments. Then a call to blockType.getConstructor().newInstance(), which GGrec's answer demonstrates, will return an instance of Block.
Use reflection to create a new instance from the class blueprint.
Pattern:
Class.forName(className).getConstructor().newInstance();
Your case:
blocks[i] = blockType.getConstructor().newInstance();
I want to use an Annotation in compile-safe form.
To pass the value() to the Annotation i want to use the String representation of an enum.
Is there a way to use #A with a value from enum E ?
public class T {
public enum E {
a,b;
}
// C1: i want this, but it won't compile
#A(E.a)
void bar() {
// C2: no chance, it won't compile
#A(E.a.toString())
void bar2() {
}
// C3: this is ok
#A("a"+"b")
void bar3() {
}
// C4: is constant like C3, is'nt it ?
#A(""+E.a)
void bar4() {
}
}
#interface A {
String value();
}
Update
I need the String type in #A.
The point is i can do this
#A("" + 1)
void foo() {
}
But here the compiler claims "attribute value must be constant". Is'nt E.a constant ?
#A("" + E.a)
void foo() {
}
The problem is that you're smarter than the compiler :-)
E.a is a constant, but E.a.toString() is not. It looks like it should be, but the compiler can't figure that out.
The reason why "a"+"b" and "" + 1 work is that the compiler is smart enough to generate the constants at compile time.
When it sees "" + E.a, it uses E.a.toString(). The call to toString() is enough to throw it off.
Does E have to be an enum? You could try:
public final class E {
public static final String a = "a";
public static final String b = "b";
};
Make the value in the annotation of type E:
#interface A {
E value();
}
Then you can use
#A(E.a)