Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java enums in generic type

I'd like to create a generic enum-based mapper for IBatis. I'm doing this with the below code. This does have compile time errors, which I don't know how to fix. Maybe my solution is just plain wrong (keep in mind the use of IBatis), in such case please suggest something better.

Any help appreciated.

What I want to achieve is to define subsequent mappers as:

public class XEnumTypeHandler extends CommonEnumTypeHandler<X> {
}

The current code:

public class CommonEnumTypeHandler<T extends Enum> implements TypeHandlerCallback {

 public void setParameter(ParameterSetter ps, Object o) throws SQLException {
  if (o.getClass().isAssignableFrom(**T**)) { 
   ps.setString(((**T**) o).value().toUpperCase());
  } else
   throw new SQLException("Excpected ParameterType object than: " + o);
 }

 public Object getResult(ResultGetter rs) throws SQLException {
  Object o = valueOf(rs.getString());
  if (o == null)
   throw new SQLException("Unknown parameter type: " + rs.getString());
  return o;
 }

 public Object valueOf(String s) {
  for (T pt : T.**values()**) {
   if (pt.**value()**.equalsIgnoreCase(s))
    return pt;
  }
  return null;
 }
}

I've added error markings to the above code, the error messages are in order:

  • T cannot be resolved
  • The method value() is undefined for the type T
  • The method values() is undefined for the type T
  • The method values() is undefined for the type T

I've solved this issue with the following code:

public class CommonEnumTypeHandler<T extends Enum> implements TypeHandlerCallback {

    Class<T> clazz;

    public CommonEnumTypeHandler(Class<T> clazz) {
        this.clazz = clazz;
    }

    public void setParameter(ParameterSetter ps, Object o) throws SQLException {
        if (o.getClass().isAssignableFrom(clazz)) {
            ps.setString(((T) o).name().toUpperCase());
        } else
            throw new SQLException("Excpected " + clazz + " object than: " + o);
    }

    public Object getResult(ResultGetter rs) throws SQLException {
        Object o = valueOf(rs.getString());
        if (o == null)
            throw new SQLException("Unknown parameter type: " + rs.getString());
        return o;
    }

    public Object valueOf(String s) {
        return Enum.valueOf(clazz, s);
    }
}

Inheriting from this class I do:

public class SalesChannelTypeHandler extends CommonEnumTypeHandler<SalesChannel> {

    public SalesChannelTypeHandler() {
        super(SalesChannel.class);
    }

    public SalesChannelTypeHandler(Class<SalesChannel> clazz) {
        super(clazz);
    }

}
like image 794
Marcin Cylke Avatar asked Jun 15 '10 11:06

Marcin Cylke


People also ask

Can you use == for enums?

Because there is only one instance of each enum constant, it is permissible to use the == operator in place of the equals method when comparing two object references if it is known that at least one of them refers to an enum constant.

Can enums be compared == Java?

There are two ways for making comparison of enum members : equals method uses == operator internally to check if two enum are equal. This means, You can compare Enum using both == and equals method.

Can enums be inherited Java?

As we have learned, we cannot inherit enum classes in Java. However, enum classes can implement interfaces.

Do Java enums start at 0 or 1?

enum starts always with 0.


2 Answers

I'm not sure what you're doing (a general overview in words would be nice), but:

  • You can't do isAssignableFrom(T) (you need a Class object), and you can't do instanceof T either (generics are non-reified). You may want to pass Class<T> type tokens instead.
  • Have you looked at EnumMap?

See also

  • Java Tutorials/Runtime Type Tokens
  • Neal Gafter's Blog - Super Type Tokens
  • Josh Bloch - Typesafe Heterogenous Container (THC) pattern (PDF)

It's still not clear what is desired, but perhaps it's something along the lines of this:

enum Color { BLACK, WHITE; }

public static void main(String[] args) {
    Color c = Enum.valueOf(Color.class, "black".toUpperCase());
    System.out.println(c); // prints "BLACK"
}

So we use Enum.valueOf that takes a type token Class<T extends Enum<T>>, and ask it for the enum constant with a given name. valueOf is NOT case-insensitive, but by conventions, all constants should be in uppercase, so we simply take the query string and turn it .toUpperCase().

like image 164
polygenelubricants Avatar answered Oct 06 '22 13:10

polygenelubricants


As pointed by Polygenelubricants, you need to pass concrete runtime types around, e.g. Class<?> instead of syntactic compiletime types like generic parameters. Here's a rewrite how you could use it:

public abstract class CommonEnumTypeHandler<E extends Enum<E>> implements TypeHandlerCallback {
    private Class<E> enumClass;

    public CommonEnumTypeHandler(Class<E> enumClass) {
        this.enumClass = enumClass;
    }

    public void setParameter(ParameterSetter ps, Object o) throws SQLException {
        if (enumClass.isInstance(o)) {
            ps.setString((enumClass.cast(o)).name().toUpperCase());
        } else {
            throw new SQLException("Excpected ParameterType object than: " + o);
        }
    }

    public Object getResult(ResultGetter rs) throws SQLException {
        try {
            return Enum.valueOf(enumClass, rs.getString());
        } catch (IllegalArgumentException e) {
            throw new SQLException("Unknown parameter type: " + rs.getString(), e);
        }
    }
}

Which you can then use as follows:

public class XEnumTypeHandler extends CommonEnumTypeHandler<X> {
    public XEnumTypeHandler() {
        super(X.class);
    }
}
like image 36
BalusC Avatar answered Oct 06 '22 12:10

BalusC