Help me understand generics. Say I have two enums as inner classes like so:
public class FoodConstants {
public static enum Vegetable {
POTATO,BROCCOLI,SQUASH,CARROT;
}
public static enum Fruit {
APPLE,MANGO,BANANA,GUAVA;
}
}
Instead of having both enums implement an interface, and have to implement the same method twice, I would like to have a method in the outer class that does something like:
public <e> String getEnumString<Enum<?> e, String s) {
for(Enum en: e.values()) {
if(en.name().equalsIgnoreCase(s)) {
return s;
}
}
return null;
}
However this method does not compile. What I am trying to do is find out if a string value is the name of an enumerated value, in ANY enum, whether it's Vegetable, Fruit, what not.
Regardless of whether this is in fact a redundant method, what is wrong with the one I am trying to (re)write?
Basically I would like to do this:
public class FoodConstants {
public static enum Vegetable {
POTATO,BROCCOLI,SQUASH,CARROT;
}
public static enum Fruit {
APPLE,MANGO,BANANA,GUAVA;
}
public <e> String getEnumString<Enum<?> e, String s) {
for(Enum en: e.values()) {
if(en.name().equalsIgnoreCase(s)) {
return s;
}
}
return null;
}
} //end of code
public static <E extends Enum<E>>
String getEnumString(Class<E> clazz, String s){
for(E en : EnumSet.allOf(clazz)){
if(en.name().equalsIgnoreCase(s)){
return en.name();
}
}
return null;
}
The original has a few problems:
It accepts an instance of the enum instead of the class representing the enum
which your question suggests you want to use.
The type parameter isn't used.
It returns the input instead of the instance name. Maybe returning the instance would be more useful -- a case-insensitive version of Enum.valueOf(String).
It calls a static method on an instance so you can iterate. EnumSet does all the reflective stuff for you.
Related
I have the following Enum where I would like to create a method that will accept an Enum object, and return a comma separated list of the properties of that Enum. So for the following Enum, my method would accept it as a parameter and return a String of "1, 2".
public class typeEnum {
public enum validTypes{
TYPE1("1"),
TYPE2("2");
private String value;
validTypes(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public static boolean contains(String type) {
for (validTypes msgType : validTypes.values()) {
if (msgType.value.equals(type)) {
return true;
}
}
return false;
}
}
}
My expected method would be something like the following:
public static <E extends Enum<E>, S extends String> String enumPropsToString(E enumClazz, S value) {
return ....
}
Enums can implement interfaces:
public interface ValuedEnum {
public String getValue();
}
public class SomeEnum implements ValuedEnum {
//your body from above
}
Then, you can use "intersection types" in your method signature:
public <T extends Enum<T> & ValuedEnum> String propsToString (Class<T> enumClass) {
Arrays.stream(enumClass.getEnumConstants()).map(e -> e.getValue()).collect(Collectors.joining(", "));
}
You could also do this via reflection - inspect the enumClass parameter of an arbitrary Enum for a Method with the getValue signature you want, and then reflectively call it; but this way is type-safe and sufficiently trivial that I'd strongly recommend doing this instead.
Reading your comments, if you want to allow an arbitrary property, then I'd suggest just using the Stream API:
public static <T> String propsToString (T[] values, Function<T, String> extractor) {
return Arrays.stream(values).map(extractor).collect(Collectors.joining(", "));
}
which can be invoked like this:
propsToString(MyEnum.values(), MyEnum::name);
Here is the method you need:
private static <E extends Enum<E>> String enumValues(Class<E> clazz) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Method m = clazz.getMethod("values");
Object[] values = (Object[])m.invoke(null);
Method f = clazz.getMethod("getValue");
StringBuilder sb = new StringBuilder(f.invoke(values[0]).toString());
for (int i = 1 ; i < values.length ; i++) {
sb.append(", ");
sb.append(f.invoke(values[i]).toString());
}
return sb.toString();
}
Usage:
enum MyEnum {
X("A"), Y("B");
public String value;
MyEnum(String value) {
this.value = value;
}
}
// ...
System.out.println(enumValues(MyEnum.class));
// prints "A, B"
Explanation of how this works:
Instead of passing an enum instance to the method, you should pass a Class<E>. This makes it easier for reflecting stuff from it. It also makes it easier to call the method as you don't need to create a new enum instance.
First, we need to get all the values of the enum class passed in. To do this we get the method values from the enum class and call it. Here I stored all the values in a variable called values.
I then got the method getValue from the enum class. In the comments you said that when an enum does not have a value field it should not be passed into this method. I assume you mean you don't care about non-existent value fields and you trust the caller. That's why I didn't do any checks here.
After that I used a string builder and for loop to concatenate the value of value for each of the enum values.
Phew that was long!
Note that this method is extremely unsafe. You should probably use interfaces like the other answers have said. But hey, this is fun, and you seem confident that you won't pass anything invalid in there. :)
This might look similar to a few other questions but so far I have not found a solution..
I am using reflection to parse my JSONs into different classes and it saves me a lot of effort from writing class-specific parsing code, all the ints, longs, strings, and Calendars etc are easy to deal with, but now I find myself in an hell of Enum specific castings
something like:
else if (field.getType().isAssignableFrom(TransactionType.class)){
field.set(representation, TransactionType.fromInt(Integer.parseInt(value, 10)));
}
the problem is that enums are stored as integers in the JSON, and I can not find a generic way to parse or cast those integers back to enums when I don't know what enum it specifically is, and I have quite a few enums so 70% of my parsing code are now dedicated to checking enum types...
is there a way that, given only field.getType().isEnum() == true, parse the int value to the enum type of that field
the enum type is declared as:
public static enum TransactionType{
cashback(0),deposit(1),withdraw(2),invitation(3);
public int code;
TransactionType(int code){
this.code = code;
}
private final static TransactionType[] map = TransactionType.values();
public static TransactionType fromInt(int n){
return map[n];
}
}
the JSON can be a bit complicated, but enum related fields has formats as:
{transactionType: 1, someOtherEnumType: 0}
Heres how I would approch this given the information provided. Use a helper method that would sit outside your enum types that can convert any enum type that implements some interface.
public static interface Codeable {
public int getCode();
}
public static enum TransactionType implements Codeable {
cashback(0),deposit(1),withdraw(2),invitation(3);
public int code;
TransactionType(int code) {
this.code = code;
}
#Override
public int getCode() {
return code;
}
}
public static <T extends Codeable> T fromCodeToEnum(int code, Class<T> clazz) {
for(T t : clazz.getEnumConstants()) {
if(t.getCode() == code) {
return t;
}
}
return null;
}
public static void main(String [] args) {
TransactionType type = fromCodeToEnum(1, TransactionType.class);
System.out.println(type); // deposit
}
Edit: Or of course you can just get the enum values and iterate through them. This could be placed wherever you want.
public static TransactionType findTransactionTypeByCode(int code) {
for(TransactionType t : TransactionType.values()) {
if(t.getCode() == code) {
return t;
}
}
return null;
}
Java do not support the implicit cast from literal to value.
Then enum in Java has method ordinal(), that returns int value.
Returns the ordinal of this enumeration constant (its position in its enum declaration, where the >initial constant is assigned an ordinal of zero).
An unsafe solution
if(field.getType().isEnum()) {
Object itemInstance = field.getType().getEnumConstants()[ordinal];
}
How ever it is not recommended to us it at it was designed as part of API.
The recommendation for this case is to define in the definition of enum.
enum MyEnum {
ITEM(1);
private final int index;
MyEnum(int index) {
this.index;
}
}
And then you should implement additional logic to serialize and deserialize, based for example on interface with default method.
interface SerializableEnum<E extends Enum<E>> {
Class<E> getType();
default E valueOf(int ordinal) {
return getType().getEnumConstants()[ordinal];
}
}
Note that the best solution is to serialize the enum not via number but via its name.
Class.getEnumConstants() is what you need.
Class<?> cls = field.getType();
if (cls.isEnum()) {
field.set(representation,
cls.getEnumConstants()[Integer.parseInt(value, 10)]);
}
I have a class that should accept different datatypes as the second constructor parameter:
public abstract class QueryMatch {
String key;
Object input;
public <T> QueryMatch(String key, T o) {
this.key = key;
input = o;
}
public String getKey() {
return key;
}
public Object getValue() {
return input;
}
}
I don't want to use type parameters, like
public abstract class QueryMatch<T>{
String key;
T input;
...
As this way I'm getting raw types warnings when declaring retrieving QueryMatch as a generic (as I don't know the datatype it contains). But the problem is that I need to return the value and I'm not totally comfortable by returning an Object (is that just me, but it doesn't seem like a good practice?).
Additionally, another class inherits from it:
public class QueryMatchOr extends QueryMatch {
public QueryMatchOr() {
super("title", new ArrayList<String>());
}
public void addMatch(String match) {
((ArrayList<String>) input).add(match);
}
}
And of course I'm getting a Unchecked cast warning (which I can avoid with #SuppressWarnings(“unchecked”)).
So, my question is... is there a better way to achieve what I'm trying to do? An abstract class that contains an object (which could be bounded), and returning the datatype it contains (instead of an Object) without using a type parameter in the class declaration?
What you are doing is not a good design. You are using an Object type field from the superclass while you only can know it's actual (needed) type in the subclass. If you only know that in the subclass, declare that variable in the subclass. Not even to mention that your fields are not private.
How about:
public abstract class QueryMatch {
private String key;
public QueryMatch(String key) {
this.key = key;
}
public String getKey() {
return key;
}
public abstract void addMatch(String match);
}
public class QueryMatchOr extends QueryMatch {
private ArrayList<String> input;
public QueryMatchOr() {
super("title");
input = new ArrayList<String>();
}
public void addMatch(String match) {
input.add(match);
}
}
If you need the getValue() method in the superclass, you really should make it generic:
public abstract class QueryMatch<T> {
private String key;
public QueryMatch(String key) {
this.key = key;
}
public String getKey() {
return key;
}
public abstract void addMatch(String match);
public abstract T getValue();
}
public class QueryMatchOr extends QueryMatch<ArrayList<String>> {
private ArrayList<String> input;
public QueryMatchOr() {
super("title");
input = new ArrayList<String>();
}
public void addMatch(String match) {
input.add(match);
}
public ArrayList<String> getValue(String match) {
input;
}
}
So first, I think the best answer is to make your class generic. But if you really don't want to do this you could do something like this:
public <T> T getValue(Class<T> type) {
return (T)input;
}
In some way you need to provide the expected type for the return value to the class. This can either be done my making that class generic or the method generic.
So, my question is... is there a better way to achieve what I'm trying to do?
No, there isn't.
I think you should use generics instead of #SuppressWarnings(“unchecked”))
I'm wondering what are the options to specialize generic types in Java, i.e. in a templated class to have specific overrides for certain types.
In my case I was a generic class (of type T) to return null usually, but return "" (the empty string), when T is the String type, or 0 (zero) when its the Integer type, etc.
Merely providing a type-specific overload of a method produces a "method is ambiguous" error:
e.g.:
public class Hacking {
public static void main(String[] args) {
Bar<Integer> barInt = new Bar<Integer>();
Bar<String> barString = new Bar<String>();
// OK, returns null
System.out.println(barInt.get(new Integer(4)));
// ERROR: The method get(String) is ambiguous for the type Bar<String>
System.out.println(barString.get(new String("foo")));
}
public static class Bar<T> {
public T get(T x) {
return null;
}
public String get(String x) {
return "";
}
}
}
Is the only option to subclass the generic class with a specific type (see StringBar in the following example?
public static void main(String[] args) {
Bar<Integer> barInt = new Bar<Integer>();
StringBar barString2 = new StringBar();
// OK, returns null
System.out.println(barInt.get());
// OK, returns ""
System.out.println(barString2.get());
}
public static class Bar<T> {
public T get() {
return null;
}
}
public static class StringBar extends Bar<String> {
public String get() {
return "";
}
}
}
Is this is the only way, it's a bit of a pain to have to create a subclass for every type I want to specialize instead of an overload of get() in the Bar class.
I'm guessing I could check the instanceof in the Bar.get() method, e.g.
T get(T t) {
if (t instanceof String) return "";
if (t instanceof Integer) return 0;
else return null;
}
However I've been taught to avoid instanceof and use polymorphism when possible.
All things considered, the concensus appears to be that the StringBar method mentioned in the question is the only way to go.
public static class StringBar extends Bar<String> {
public String get() {
return "";
}
}
Generics in Java are very different from templates in C++ in this respect. It is not possible to write a specific version of a generic class to do something different for a particular case, as C++ can do. It is also not possible to determine at run time what T is - this is because that information is not passed into the byte code (object code) and so doesn't even exist at runtime. This due to something called "type erasure".
BarString and BarInt would be the obvious way of doing this, but there are improvements you can make. For example you can write a generic Bar to cover the common cases, and then write specialized BarString and BarInt to implement special cases. Ensure that the instances can only be created through a factory, which takes the class of the object to be processed:
class Bar<T> {
class BarString extends Bar<String> {
// specialist code goes here
}
static Bar<T> createBar(Class<T> clazz) {
if (clazz==String.class) {
return new BarString();
} else {
return new Bar<T>;
}
That probably won't compile, but I don't have the time to work out the exact syntax. It does illustrate the principle.
The compiler is actually correct, because the following code is compile-time checked (Bar<String> barString = new Bar<String>();) when compiled, from
public static class Bar<T> {
public T get(T x) {
return null;
}
public String get(String x) {
return "";
}
}
to
public static class Bar<String> {
public String get(String x) {
return null;
}
public String get(String x) {
return "";
}
}
and is ambiguous as you can't have 2 identical methods with the same return types and the same parameter arguments.
See an explanation by Jon Skeet's:
What is the concept of erasure of generics in java?
Java Generics - Types erasures - when and what happens?
You can subclass Bar<T> and create StringBar (note I removed the static keyword) and override get() method.
public class BarString extends Bar<String> {
#Override
public String get(String x) {
return "";
}
}
Generics in Java aren't made for specialization. They're made for generalization! If you want to specialize for certain types, you should be specializing...through a subclass.
Often you don't need to do something in a specialized manner however. Your StringBar example is kind of contrived because you could have this:
public class Bar<T> {
private final T value;
public T get() {
return value;
}
}
I don't see why you need to specialize for a String here.
Is there a way to write a generic loop to iterate over an arbitrary Enum?
For example:
public static void putItemsInListBox(Enum enum, ListBox listbox){
for(Enum e : enum.values(){
listBox.addItem(e.toString(), String.valueOf(e.ordinal());
}
}
You can not do the above, because the Enum class does not have a method called values() like the implemented Enum classes. The above for loop works fine for a class that is defined as an enum.
It works exactly the same way as if the Class is passed:
public static <E extends Enum<?>> void iterateOverEnumsByInstance(E e)
{
iterateOverEnumsByClass(e.getClass());
}
public static <E extends Enum<?>> void iterateOverEnumsByClass(Class<E> c)
{
for (E o: c.getEnumConstants()) {
System.out.println(o + " " + o.ordinal());
}
}
Usage:
enum ABC { A, B, C }
...
iterateOverEnumsByClass(ABC.class);
iterateOverEnumsByInstance(ABC.A);
This is cheap, but should work (at least according to my testing):
public static <T extends Enum<T>> void putItemsInListBox(Class<T> cls, ListBox listbox) {
for (T item : EnumSet.allOf(cls))
listbox.addItem(item.toString(), String.valueOf(item.ordinal()));
}
This works because EnumSet has special magical access to non-public members of Enum, which allows it to enumerate the enum's values despite the lack of a values method.