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?
Related
class MyObject{}
class Human extends MyObject{}
public ConcurrentSkipListMap<String, <T extends MyObject>> get(Class<T> clazz, Object... vars){
//...
}
//use the get() with
ConcurrentSkipListMap<String, Human> mapOfAllAdams = get(Human.class, "Adam");
However, the return value ConcurrentSkipListMap<String, <T extends MyObject>> gives compilation error, but <T extends MyObject>T is ok. What should I write instead?
Make these changes
class MyObject{}
class Human extends MyObject{}
public <T extends MyObject> ConcurrentSkipListMap<String, T> get(Class<T> clazz, Object... vars){
//...
return null;
}
//use the get() with
void hello(){
ConcurrentSkipListMap<String, Human> x = get(Human.class, "Adam");
}
I declared <T extends MyObject> before the return type and also added a variable x to hold the return value of the get function.
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.
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()
What I would like to have is something like this:
public abstract Class Content {
private Map<Class<? extends Content>, List<? extends Content>> relations;
}
Content has a bunch of subclasses - A,B,C,D etc...
The most frequent use case is to get all A's:
public List<A> getA() {
return (List<A>)relations.get(A.class);
}
Kind of ok - apart from the ugly cast.
But the real problem is there's nothing stopping me from doing something stupid like:
relations.put(A.class, List<B> myListOfBs);
So a call to getA() above would result in a horrible cast exception. Is there any way I can write it so the compiler would warn me in the above example - and also remove the need for the ugly cast.
Thanks
You can create a wrapper around a Map and use a generic method to constrain your put method:
public class HeterogeneousContainer {
private final Map<Class<? extends Content>, List<? extends Content>> map;
public <T extends Content> void put(Class<T> type, List<T> values) {
map.put(type, values);
}
public <T extends Content> List<T> get(Class<T> type) {
//this warning can be safely suppressed after inspection
return (List<T>) map.get(type);
}
}
Now you know that as long as the users of the container aren't using it improperly (i.e. in raw form) then the key and value must correspond...you couldn't call put(B.class, listOfAs);.
Create a custom Map interface that fixes the type to the same type:
interface RelatedMap<T> extends Map<Class<T>, List<T>> {}
then
private RelatedMap<? extends Content> relations;
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());
}
}