Generics when implementing Spring's ConverterFactory with Enums - java

From the Spring reference documentation, a converter factory can be implemented as follows:
final class StringToEnumConverterFactory implements ConverterFactory<String, Enum> {
public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
return new StringToEnumConverter(targetType);
}
private final class StringToEnumConverter<T extends Enum> implements Converter<String, T> {
private Class<T> enumType;
public StringToEnumConverter(Class<T> enumType) {
this.enumType = enumType;
}
public T convert(String source) {
return (T) Enum.valueOf(this.enumType, source.trim());
}
}
}
However, Enum is a raw type here. If I parameterize Enum and have my IDE (Eclipse Mars) add the method, it results in the following:
final class StringToEnumConverterFactory<T extends Enum<T>> implements ConverterFactory<String, Enum<T>> {
#Override
public <T extends Enum<T>> Converter<String, T> getConverter(Class<T> targetType) {
return new StringToEnumConverter<T>(targetType);
}
private final class StringToEnumConverter<T extends Enum<T>> implements Converter<String, T> {
private Class<T> enumType;
public StringToEnumConverter(Class<T> enumType) {
this.enumType = enumType;
}
public T convert(String source) {
return Enum.valueOf(this.enumType, source.trim());
}
}
}
But now I have two issues:
The following compiler error appears:
The type StringToEnumConverterFactory<T> must implement the inherited abstract method ConverterFactory<String,Enum<T>>.getConverter(Class<T>)
The type parameter T is hiding the type T.
Can you someone please explain how to properly change the StringToEnumConverterFactory to have Enum parameterized?

This actually turns out to be (perhaps surprisingly, not sure) a pain in the neck, because of the way Enum.valueOf is declared.
To get the example as-is to compile, you'd end up with something like this:
class StringToEnumConverterFactory<T extends Enum<T>>
implements ConverterFactory<String, T> {
// ^
#Override
public <E extends T> Converter<String, E> getConverter(Class<E> targetType) {
// ^^^^^^^^^^^ ^ ^
return new StringToEnumConverter<E>(targetType);
// ^
}
// ...
}
This, unfortunately, doesn't actually help us out because then you can only ever have e.g. a StringToEnumConverterFactory<Planet> or StringToEnumConverterFactory<Color> which defeats the purpose of the generalization the generic method offers. And you won't be able to make a StringToEnumConverterFactory<?> or StringToEnumConverter<Enum<?>>.
We don't actually want a type variable declared on the class, so the declaration we desire is like this, with a wildcard:
class StringToEnumConverterFactory
implements ConverterFactory<String, Enum<?>> {
#Override
public <E extends Enum<?>> Converter<String, E> getConverter(
Class<E> targetType) {
return new StringToEnumConverter<E>(targetType);
}
// ...
}
But this creates problems for us when we try to call Enum.valueOf because its type variable declaration is more restrictive. We won't ever be able to call it without some sort of cowboy unchecked cast (which is provably safe, but only with Enum):
return (T) Enum.valueOf((Class) enumType, source);
Fortunately, we can still otherwise bypass this through the Class object:
for (T constant : enumType.getEnumConstants())
if (constant.name().equals(source))
return constant;
The final code would be something like this:
class StringToEnumConverterFactory
implements ConverterFactory<String, Enum<?>> {
#Override
public <E extends Enum<?>> Converter<String, E> getConverter(
Class<E> targetType) {
return new StringToEnumConverter<E>(targetType);
}
static class StringToEnumConverter<E extends Enum<?>>
implements Converter<String, E> {
Class<E> enumType;
StringToEnumConverter(Class<E> enumType) {
this.enumType = enumType;
}
#Override
public E convert(String source) {
source = source.trim();
// perhaps we would cache the constants somewhere
for (E constant : enumType.getEnumConstants())
if (constant.name().equals(source))
return constant;
// also some people like to return null
throw new IllegalArgumentException(source);
}
}
}
So no raw types, no unchecked casts, but kind of a pain in the neck.

I think it may not be possible to define a converter factory as you are trying to do. The type you really want is a ConverterFactory<String, R>, where R extends Enum<R>. Note that I have used the type R here to match the documentation, in order to avoid confusion.
The API of the ConverterFactory interface then requires us to implement the following method:
public <T extends R> Converter<String, T> getConverter(Class<T> targetType) {
???
}
We know that it is in fact impossible to extend enum types, and therefore the only possibility is that T and R are the same type. However this is not known by the compiler, and therefore we cannot make any other assertions about the type T other than that it extends R.
In particular, we do not know whether T extends Enum<T>. We therefore cannot use T in a StringToEnumConverter<T> as you have defined it, since we cannot use Class<T> in Enum.valueOf.
I think you therefore need to continue using the original version without generics. It is slightly unfortunate that it requires an unchecked cast, but keep in mind that generics are erased at compile time, so all that will be left is a cast to Enum anyway.

Related

Java: Method returns Class<T> (where <T extends BaseType>). Returning subclass class causes type mismatch error

Long time reader, first time caller (on this board anyway).
Envision the following:
public class BaseClass{ }
public class C1 extends BaseClass { }
public class C2 extends BaseClass { }
Now, I have a method:
private static <T extends BaseClass> Class<T> getBaseClassType(boolean b) {
if (b) {
return C1.class;
} else {
return C2.class;
}
}
I would think this is valid since both C1.class C2.class fulfill the type declaration <T extends BaseClass>.
However, the 'return' statements are tagged with "Type mismatch: cannot convert from Class<C1> to Class<T>".
Why is that? Can the method above be re-expressed in a manner to not require casts?
I am able to cast the returned values like this:
private static <T extends BaseClass> Class<T> getBaseClassType(boolean b) {
if (b) {
return (Class<T>) C1.class;
} else {
return (Class<T>) C2.class;
}
}
...but this generates warnings "Type safety: Unchecked cast from Class to Class" (which, of course, can be suppressed). I'm confused as to why this is necessary since C1 (or C2) extends BaseClass.
What's the "right" way to do this to make the editor/compiler happy?
thanks!
Turns out the solution is to remove the explicit type and substitute an unknown type specification:
private static Class<? extends BaseClass> getBaseClassType(boolean b) {
if (b) {
return (Class<T>) C1.class;
} else {
return (Class<T>) C2.class;
}
}
I found this after searching around some of the 'related' answers.
You have to adjust the class return type as shown below.
private static Class<? extends BaseClass> getBaseClassType(boolean b) {
if (b) {
return C1.class;
} else {
return C2.class;
}
}
The problem is that a cast is a runtime check and compile time its not known what would be type of T that why its raising warning to check the type of class before cast for safer side.
Use #SuppressWarnings("unchecked") to suppress warning -
#SuppressWarnings("unchecked")
Method signature
or add unknown type as
private static Class<? extends BaseClass> getBaseClassType(boolean b)

Interesting unchecked cast warning

Given this code:
public final class MyMap extends HashMap<MyClass<? extends MyInterface>, MyInterface>
{
public <T extends MyInterface> T put(MyClass<T> key, T value)
{
return (T)key.getClass().getComponentType().cast(super.put(key, value));
}
}
I get an unchecked cast warning on the cast from MyInterface to T.
I really dont like it so I use the Class.cast() method to cast it instead.
Now my new function looks like this:
public final class MyMap extends HashMap<MyClass<? extends MyInterface>, MyInterface>
{
public <T extends MyInterface> T put(MyClass<T> key, T value)
{
Class<T> cl = (Class<T>)key.getClass().getComponentType();
return cl.cast(super.put(key, value));
}
}
But this one gives an unchecked cast warning from Class<?> to Class<T> as Object.getClass() returns a wildcard class.
However, is there any chance that key.getClass().getComponentType() will not return Class<T> as T is defined by the component type of the class of key?

Generics and Class<? extends Enum<?>>, EnumSet.allOf(class) vs class.getEnumConstants()

I have the following BeanValidation code that works fine, and permits to validate that a bean annotated with:
#EnumValue(enumClass = MyTestEnum.class)
private String field;
public enum MyTestEnum {
VAL1, VAL2;
}
Will be validated only if the field value is "VAL1" or "VAL2".
public class EnumNameValidator implements ConstraintValidator<EnumValue, String> {
private Set<String> AVAILABLE_ENUM_NAMES;
#Override
public void initialize(EnumValue enumValue) {
Class<? extends Enum<?>> enumSelected = enumValue.enumClass();
Set<? extends Enum<?>> enumInstances = Sets.newHashSet(enumSelected.getEnumConstants());
AVAILABLE_ENUM_NAMES = FluentIterable
.from(enumInstances)
.transform(PrimitiveGuavaFunctions.ENUM_TO_NAME)
.toImmutableSet();
}
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if ( value == null ) {
return true;
} else {
return AVAILABLE_ENUM_NAMES.contains(value);
}
}
}
What I don't understand is why my first attempt failed. Using instead of the enumSelected.getEnumConstants() above the following code:
Set<? extends Enum<?>> enumInstances = EnumSet.allOf(enumSelected);
Intellij 12 doesn't highlight any error, but the compiler says:
java: method allOf in class java.util.EnumSet<E> cannot be applied to given types;
required: java.lang.Class<E>
found: java.lang.Class<capture#1 of ? extends java.lang.Enum<?>>
reason: inferred type does not conform to declared bound(s)
inferred: capture#1 of ? extends java.lang.Enum<?>
bound(s): java.lang.Enum<capture#1 of ? extends java.lang.Enum<?>>
I don't understand the problem, and I also have that code which works fine:
private static <T extends Enum<T> & EnumAlternativeName> T safeGetByAlternativeName(Class<T> enumClass, String alternativeName) {
for ( T t : EnumSet.allOf(enumClass) ) {
if ( t.getAlternativeName().equals(alternativeName) ) {
return t;
}
}
return null;
}
My guess is that in ? extends Enum<?> the two ? could be different whereas allOf expects a T extends Enum<T> where both T are the same.
For example, consider the following code:
static enum MyEnum {}
static class EnumValue<T extends Enum<T>> {
Class<T> enumClass;
EnumValue(Class<T> enumClass) {
this.enumClass = enumClass;
}
Class<T> enumClass() { return enumClass; }
}
These lines will compile:
EnumValue<?> enumValue = new EnumValue(MyEnum.class); // raw constructor
Set<? extends Enum<?>> enumInstances = EnumSet.allOf(enumValue.enumClass());
because we know that the two T in enumValue.enumClass() are the same but this won't:
EnumValue enumValue = new EnumValue(MyEnum.class);
Class<? extends Enum<?>> enumSelected = enumValue.enumClass();
Set<? extends Enum<?>> enumInstances = EnumSet.allOf(enumSelected);
because you have lost information by using a Class<? extends Enum<?>> as an intermediate step.
My explanation on #assylias's solution:
What we want to express about the type of the class is that it's a
Class<E>, for some E, that E <: Enum<E>
but Java does not allow us to introduce a type variable E in a method body.
Usually, we can exploit wildcard and wildcard capture to introduce a hidden type variable
class G<T extends b(T)> { ... } // b(T) is a type expression that may contain T
G<? extends A> --capture--> G<T>, for some T, that T <: A & b(T)
But this won't work in our case, since T in Class<T> does not have a bound that makes it work.
So we need to introduce a new type with the desired bound
class EnumClass<E extends Enum<E>> // called EnumValue in assylias's solution
EnumClass(Class<E> enumClass)
Class<E> enumClass()
EnumClass<?> --capture--> EnumClass<E>, for some E, that E <: Enum<E>
We then call EnumClass<E>.enumClass() to yield a
Class<E>, for some E, that E <: Enum<E>
which is the goal we've been trying to achieve.
But how can we call the constructor of EnumClass? The origin of the problem is that we don't have a proper type for enumClass, yet the constructor of EnumClass wants a properly typed enumClass.
Class<not-proper> enumClass = ...;
new EnumClass<...>(enumClass); // wont work
Fortunately(?) the raw type helps here which disables generics type checking
EnumClass raw = new EnumClass(enumClass); // no generics
EnumClass<?> wild = raw;
So the minimum gymnastics we need to perform to cast the class to the desired type is
((EnumClass<?>)new EnumClass(enumClass)).enumClass()

Java method returns an enum which implements an interface

How can I specify that a Java method returns an enum which implements an interface?
This method is given:
public <T extends Enum<T> & SomeInterface> void someMethod(Class<T> type) {
}
And I want to do:
someMethod(anotherMethod());
What should be the signature of anotherMethod?
If someMethod expects a Class<T> parameter, where T extends Enum<T> & SomeInterface, then that's what you need to return from anotherMethod. And since you don't have anything in the parenthesis in your desired call, I would say simply:
public Class<T extends Enum<T> & SomeInterface> anotherMethod()
Correct Implementation should be
<T extends Enum<? extends T> & SomeInterface> void someMethod(Class<T> type);
<T extends Enum<? extends T> & SomeInterface> Class<T> anotherMethod();
Please check More Fun with Wildcards
Or More Simpler Version
public class Example<T extends Enum<T> & SomeInterface> {
public void someMethod(Class<T> type) {}
public Class<T> anotherMethod() {}
}
anotherMethod just needs to return a Class, it could be the exact class you have in mind that extends your enum and interface, or just a wildcard Class<?> (though you'll have compile time warnings). If you want to avoid the warnings, the return type needs to be Class<T> with a generics definition of T as in the method you're calling.

Overriding method with generic return type

Let's say I have a super-class that defines the following abstract method
public abstract <T extends Interface> Class<T> getMainClass();
Now if I want to override it in some sub-class
public Class<Implementation> getMainClass(){
return Implementation.class;
}
I get a warning about type safety and unchecked conversion:
Type safety: The return type Class<Implementation> for getMainClass() from the type SubFoo needs unchecked conversion to conform to Class<Interface> from the type SuperFoo
Doesn't Class<Implementation> fall under Class<T> if <T extends Interface>? Is there any way to properly get rid of the warning?
the overriding method's return type must be a subtype of the overridden method's return type.
Class<Impl> is not a subtype of Class<T> where <T extends Interface>. T is unknown here.
Class<Impl> is a subtype of Class<? extends Interface>, per subtyping rules.
some subtyping rules regarding wildcards:
for any type X
A<X> is a subtype of A<? extends X>
A<X> is a subtype of A<? super X>
if S is subtype of T
A<? extends S> is a subtype of A<? extends T>
A<? super T> is a subtype of A<? super S>
More concisely, ( <: means "is a subtype of" )
A<S> <: A<? extends S> <: A<? extends T>
A<T> <: A<? super T> <: A<? super S>
Consider the following scenario similar to yours:
public class SuperFoo {
public abstract <T extends Interface> List<T> getList();
}
public class SubFoo extends SuperFoo {
private List<Implementation> l = new ArrayList<Implementation>();
public List<Implementation> getList() {
return l;
}
public void iterate() {
for (Implementation i: l) ...;
}
}
SubFoo subFoo = new SubFoo();
SuperFoo superFoo = subFoo;
superFoo.getList().add(new AnotherImplementation()); // Valid operation!
subFoo.iterate(); // Unexpected ClassCastException!
In this case unchecked conversion warning warns you about possibility of unexpected ClassCastException.
However, in your case, when return type is Class<...>, it's not a problem (as far as I understand), so you can legally suppress a warning:
#SuppressWarnings("unchecked")
public Class<Implementation> getMainClass(){ ... }
Another option is to make SuperFoo itself generic:
public class SuperFoo<T extends Interface> {
public abstract Class<T> getMainClass();
}
public class SubFoo extends SuperFoo<Implementation> {
public Class<Implementation> getMainClass() { ... }
}
For yet another (and perhaps the best) option see Stas Kurilin's answer.
try this
public abstract Class<? extends Interface> getMainClass();
reorganized example
by such warnings java tried prevents cases like this
class OtherImpl implements Interface{
}
A a = new B();//where A - your abstract class and B - implementation
Class<OtherImpl> other = a.<OtherImpl>getMainClass();//some broken think, But _without_ runtime exception
As #axtavt mentioned example was broken. I reorganized it.
Why do you want to have something like public abstract Class<? extends Interface> getMainClass(); rather than public abstract Interface getMainClass();?
I think you can simply return an instance of Interface, and then, if the caller wants to have access to the underlying runtime class can simply call getClass() on the returned object.
Essentially, I think you can simply do
public InterfaceImpl implements Interface {
// ...
};
public abstract class A {
public abstract Interface getMainClass();
// ...
}
public class AImpl {
return new InterfaceImpl();
}
public class Main {
public static void main(String[] args) {
AImpl aImpl = new AImpl();
Interface i = aImpl.getMainClass();
System.out.println(i.getClass());
}
}

Categories

Resources