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

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()

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)

Java Generic Object Reuse

I have a class hierarchy, and would like to build a list of attribute setters for each class in the hierarchy. The code I want to write is similar to the following:
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
class Attribute<E, T> {
public final Class<T> dataType;
public final BiConsumer<E, T> setter;
public Attribute(final Class<T> dataType, final BiConsumer<E, T> setter) {
this.dataType = dataType;
this.setter = setter;
}
}
class Foo1 {
public static final List<Attribute<Foo1, ?>> foo1Attrs = new ArrayList<>();
static {
foo1Attrs.add(new Attribute<>(String.class, Foo1::setProp1));
}
public void setProp1(final String prop1) {
}
}
class Foo2 extends Foo1 {
// The following line results in an error
public static final List<Attribute<Foo2, ?>> foo2Attrs = new ArrayList<>(foo1Attrs);
static {
foo2Attrs.add(new Attribute<>(Integer.class, Foo2::setProp2));
}
public void setProp2(final Integer prop2) {
}
}
When I compile the above, I get the error:
error: incompatible types: cannot infer type arguments for ArrayList<>
public static final List<Attribute<Foo2, ?>> foo2Attrs = new ArrayList<>(foo1Attrs);
^
reason: inference variable E has incompatible bounds
equality constraints: Attribute<Foo2,?>
lower bounds: Attribute<Foo1,?>
where E is a type-variable:
E extends Object declared in class ArrayList
I understand why I am getting the above error. My question is, what is the typically pattern employed to make the above code compile and run?
The only way I can figure out how to make this work is to create a copy constructor on the Attribute class that takes an Attribute<? extends E, T>, then duplicate the data members (pointers) stored in the Attribute class into a second memory location with a different type. This seems really heavy for what should be the equivalent of a static_cast in C++. The following changes exemplify this approach:
class Attribute<E, T> {
...
public final BiConsumer<? super E, T> setter;
...
public Attribute(final Attribute<? super E, T> other) {
this.dataType = other.dataType;
this.setter = other.setter;
}
}
class Foo2 extends Foo1 {
public static final List<Attribute<Foo2, ?>> foo2Attrs = new ArrayList<>();
static {
for (Attribute<Foo1, ?> attr : foo1Attrs)
foo2Attrs.add(new Attribute<>(attr));
...
}
...
}
To reiterate the question: Is there a better approach to address this compile-time error than that outlined above?
Since foo2Attrs can contain an Attribute<Foo1, ?> where Foo1 is a supertype of Foo2, it should be declared as type List<Attribute<? super Foo2, ?>>. This makes sense because a Foo2 object can have its attributes set by any attribute setter which targets a superclass of Foo2.
Likewise, foo1Attrs should be declared as type List<Attribute<? super Foo1, ?>>.
This gets rid of the compilation error caused by the first type parameter, but there'll still be a problem when you try to write some code like foo2Attrs.get(i).setter.accept(foo, 23), because the setter is inferred as type BiConsumer<? super Foo2, ?> and the integer 23 can't be converted to the second ?.

Java generics - cast assignable capture type to subclass

I have the following scenario in Java generics:
public abstract class A<T> {
protected final Class<T> typeOfX;
public A(final Class<T> typeOfX) {
this.typeOfX = typeOfX;
}
public abstract void load(final T x);
}
public class AnyA<S> extends A<S> {
private final Map<String, A<? extends S>> map;
public AnyA(final Class<S> superTypeOfX,
final Map<String, A<? extends S>> map) {
super(superTypeOfX);
this.map = map;
}
#Override
public void load(final S superx) {
for (final A<? extends S> a: map.values())
if (a.typeOfX.isAssignableFrom(superx.getClass())) //Here I want to say: "if superx can be casted to a.typeOfX".
a.load(a.typeOfX.cast(superx)); //Here I want to cast superx to a.typeOfX (so as to call the load method). Here's the compile error.
}
}
I'm getting the error:
incompatible types: S cannot be converted to CAP#1
where S is a type-variable:
S extends Object declared in class AnyA
where CAP#1 is a fresh type-variable:
CAP#1 extends S from capture of ? extends S
AnyA is a composite A, i.e. is an A which maintains several other A instances.
AnyA in its load(...) method shall decide which of the maintained A instances should be used to "pass the loading process to" of the argument.
In other words, AnyA is responsible for finding the correct A to load the argument.
But also AnyA is an A because it handles loading the argument.
My question is:
Why is this cast not possible, by the time I know that S is a sub-class of T and all A instances in AnyA can load a subclass of S?
How can I overcome this problem without changing the class diagram too much?
I have read about "helper methods" but cannot match the example shown there to my problem.
I'm using NetBeans IDE with Java SDK 8.
Note that regardless of what you do, the code is not "syntactically type safe" in any case. There is an unchecked cast, and the only safety belt that prevents this from going wrong is the isAssignableFrom check.
(That is often OK, I'm just mentioning it for completeness)
The reason for the error may be more obvious when you pull the lines apart (here, S stands for SuperType, according to the Type Parameter Naming Conventions - please follow them!)
A<? extends S> a = ...;
S s = a.typeOfX.cast(s);
a.load(s);
The A<? extends S> intuitively means that it is an A that can accept an unknown type in its load method. You know that it extends type S, but you do not know which type this is.
It may become blatantly obvious when you insert Object for S:
A<String> specificA = ...;
// So the "specificA" can load "String" objects. Then this is fine:
A<? extends Object> a = specificA;
Object s = a.typeOfX.cast(s);
// But here's the error: "s" is only an Object, and not a String!
a.load(s);
I think the main point of confusion (and the main reason for the question) was the following: When calling
Object s = a.typeOfX.cast(s);
and typeOfX is String.class, then the return type of the cast will not be String, but only the type that the compiler can infer at this point. And this is Object, in the example above.
However, you already referred to the Helper Methods, and indeed, with some trickery, you can make this compile,
but... (see notes below)
import java.util.Map;
abstract class A<T>
{
protected final Class<T> typeOfX;
public A(Class<T> typeOfX)
{
this.typeOfX = typeOfX;
}
public abstract void load(T x);
}
class AnyA<S> extends A<S>
{
private final Map<String, A<? extends S>> map;
public AnyA(Class<S> superTypeOfX,
Map<String, A<? extends S>> map)
{
super(superTypeOfX);
this.map = map;
}
#Override
public void load(S s)
{
for (A<? extends S> a : map.values())
{
if (a.typeOfX.isAssignableFrom(s.getClass()))
{
callLoad(a, s);
}
}
}
private static <S, T extends S> T cast(A<T> a, S s)
{
T t = a.typeOfX.cast(s);
return t;
}
private static <T, S extends T> void callLoad(A<S> a, T s)
{
a.load(cast(a, s));
}
}
I would not recommend this in practice.
Personally and subjectively: I think that when you are doing the isAssignableFrom check, then the (unchecked) cast should be as close as possible to this check. Otherwise, the code will be very hard to understand.
So although unchecked casts are a code smell in practice, and I try to avoid SuppressWarning whenever possible, I would consider this as far more readable:
for (A<? extends S> a : map.values())
{
if (a.typeOfX.isAssignableFrom(superx.getClass()))
{
// This call is safe as of the check done above:
#SuppressWarnings("unchecked")
A<Object> castA = (A<Object>) a;
castA.load(superx);
}
}

Generics when implementing Spring's ConverterFactory with Enums

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.

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