Cannot convert from not generic subclass to generic superclass - java

I'm trying to get interface instance depending on what type T is. Place or something else that extends BaseDictionary.
public static <T extends BaseDictionary> IDictionaryDataSource<T> getEntityDataSourceInstance(Class<T> clazz,
Context cxt) {
if (Place.class.isAssignableFrom(clazz)) return (IDictionaryDataSource<T>) new PlaceDataSource(cxt);
//here some other types, same lines
return null;
}
public abstract class BaseDictionary{}
public class Place extends BaseDictionary{}
public interface IDictionaryDataSource<T extends BaseDictionary>{}
public abstract class BaseDictionaryDataSource<T extends BaseDictionary> implements IDictionaryDataSource<T>{}
public class PlaceDataSource extends BaseDictionaryDataSource<Place>{}
And I get
Type mismatch: cannot convert from PlaceDataSource to IDictionaryDataSource<T>
or
Type safety: Unchecked cast from PlaceDataSource to IDictionaryDataSource<T>
if I cast it like above.
Can you explain why do compile error and warning occur?
It will be called here
public static <T extends BaseDictionary> DictionaryElementPickerFragment<T> newInstance(Class<T> clazz, Context cxt){
//somecode here
fragment.setDataSource(DictUtils.getEntityDataSourceInstance(clazz, cxt));
}
I've tried to find answer here and in google but no success.I would appreciate any help.
Now I think like this
There is no helper method to work around the problem, because the code is fundamentally wrong.
Thanks in advance.

This more concrete example illustrates your problem which is one of type parameter variance.
void foo(List<String> stringList, Integer anInteger) {
List<Object> objList = (List<Object>) stringList;
objList.add(anInteger); // Violation -- adding an object to a list of strings
// could cause someone getting a "String" to get an
// Integer stead
}
so a List<String> is not a List<Object> although it is a List<? extends Object>.
In your specific instance you can't cast
PlaceDataSource to IDictionaryDataSource<T>
PlaceDataSource is an IDictionaryDataSource<Place>, but the only thing we know about <T> is that it extends BaseDictionary which is a super-class of BaseDictionary.
So you can cast a PlaceDataSource to
an IDictionaryDataSource<Place> or to
an IDictionaryDataSource<? super Place> or to
an IDictionaryDataSource<? extends BaseDictionary>
but not to an IDictionaryDataSource<T> because T is not guaranteed to be Place, and doing so would lead to a mismatch between the actual type parameter Place and the formal type parameter T.

That is not secure implementation and it is dangerous to cast because (Place.class.isAssignableFrom(clazz) saves you here):
T is a generic type and you're replacing it with definite Place.
What if I have
public class Home extends BaseDictionary{}
public class HomeDataSource extends BaseDictionaryDataSource<Home>{}
And then I invoke getEntityDataSourceInstance with Home class but get PlaceDataSource which cannot be cast to HomeDataSource (IDictionaryDataSource) which I expect. So I'll end up having ClassCastException.

It looks like getEntityDataSourceInstance should be an instance method in the BaseDictionary class, not a static method.
A subclass will know which type of DictionaryDataSource to create.

Try #SuppressWarnings("unchecked")

Related

Unexpected behaviour with generics

I'm facing this error while work with Java generics, and I don't understand which is the problem;
I have two classes like these:
public abstract class BaseClass{
....
}
public class OneClass extends BaseClass{
....
}
I have a generic repo for OneClass:
public class MyRepo<T extends BaseClass>{
List<T> getElements();
}
Then I have a method that should works whit generics:
private MyRepo<OneClass> myRepo;
public <T extends BaseClass> List<T> myMethod(){
return myRepo.getElements();
}
The method doesn't work unless I force a cast to List ( as shown below ):
public <T extends BaseClass> List<T> myMethod(){
return (List<T>) myRepo.getElements();
}
I don't understand which is the problem given that OneClass extends BaseClass.
Thanks for any suggestion.
Having a method of the form:
<T> T myMethod()
makes the inference of the actual T dependent on the call-site:
String s = myMethod();
Integer i = myMethod();
Considering your scenario one could invoke your method like this:
List<BaseClass> a = myMethod();
List<OneClass> a = myMethod();
As you can see this can be incorrect as myMethod could actually return another subtype of BaseClass (lets say TwoClass) which is not correct to cast to List<OneClass> - thus you need the unsafe cast to List<T>.
You should change the signature of myMethod to one of the following:
public List<? extends BaseClass> myMethod(){}
public List<BaseClass> myMethod(){}
The first variant states that this is a list of any subtype of BaseClass the other just omits that information.
Dependent on what you want to achieve check the other answer or read about PECS (Producer Extends, Consumer Super) or f-bounded polymorphism / self-types to return the concrete type.
Error message tells you that not every T extends BaseClass is OneClass
You should make sure that the field myRepo is the same type as T as in your method. If you force it to be OneClass you cant use othere types except OneClass. So there is no use of a generic. If you want to allow every extending class from BaseClass you could make the class of the mehtod generic in oder to use the same type of T as shown below:
public class FunctionClass<T extends BaseClass> {
private MyRepo<T> myRepo;
public List<T> myMethod(){
return myRepo.getElements();
}
}

Generics - how to define a method that takes only classes that implement a particular interface

I've got a method that currently looks like this:
public static <T> T getBean(Class<T> clazz) {
return applicationContext.getBean(clazz);
}
At the moment it can take any type of class. I want to limit that (at compile time) so that it can only allow a limited set of classes.
So I'm thinking I can define an interface like this:
public interface GettableBean<T> {}
and change the code to this (which looks horrid with a cast):
public static <T> T getBean(GettableBean<T> clazz) {
return (T) applicationContext.getBean(clazz.getClass());
}
and what's more when I try and call it using a class that implements GettableBean
public class MyClass implements GettableBean<MyClass>
I get a compile error:
Error:(119, 27) java: method getBean in class BeanLocator cannot be applied to given types;
required: GettableBean
found: java.lang.Class
reason: cannot infer type-variable(s) T
(argument mismatch; java.lang.Class cannot be converted to GettableBean)
What am I doing wrong here ?
I don't know why you think GettableBean needs to be parameterized. It's just a marker interface.
Remove the redundant generic type parameter and add a constraint to the getBean method.
public interface GettableBean {}
public class MyClass implements GettableBean {}
public static <T extends GettableBean> T getBean(Class<T> clazz) {}
The name of this parameter should have been your clue that what you were trying was not really the right approach:
public static <T> T getBean (GettableBean<T> clazz)
A GettableBean is not a class. It's an instance. It should be called gettableBean. When you rename that, it becomes more clear that you're passing an instance only pretty much immediately disregard everything about the instance.
ONE:
The error message originates in the return statement: clazz.getClass() returns a Class<?>. You try to cast it to T. To successfully do so Class would have to be an implementation or extension of T. You could declare that by
public static <T super Class<?>> T getBean(...) ...
But that's most likely not what you want.
TWO
Actually from my understanding what you want is to allow only Class parameters for implementations of GettableBean<?>. That should look like this:
public static <T> T getBean(Class<? extends GettableBean<T>> clazz) {
return applicationContext.getBean(clazz);
}

Return ? extends Type

I want to have a method signature that looks as such:
public <? extends IDto> convertToResponseDto();
I came up with this from a valid signature that looks like:
public List<? extends IDto> convertToResponseDto();
As it turns out Type <? extends IDto> is not a valid return type. I feel like the intention of what I am trying to return is quite clear, but not sure what the correct syntax is
EDIT
This question was answered by #ErwinBolwidt, but gives a new problem in a use case. I have a case where I am using generics:
protected final <E extends IDto> E findOneInternal(final Long id) {
return getService().findOne(id).convertToResponseDto();
}
In this case, convertToResponseDto() returns IDto, and not the concrete class, as its not known at this pint because it gets the service of type T
You can override an interface method or superclass method with a more specific subtype as the return type. Say your superclass says
public IDto convertToDTO()
Then your subclass can say
public MySpecificDTO convertToDTO()
That's implied by the Java language and doesn't need any generics.
You can define a generic type
public <T extends IDto> T convertToResponseDto();

Unchecked typecasting of generic class?

I am trying to write a method where I can convert from a string to an enum object at runtime, for a generic enum. I have a method signature:
public static <T extends Enum<T>> Enum<T> foo(String string, Class<T> clazz)
However, I am calling it from a class whose generic type parameter does not explicitly extend Enum. i.e.
class bar<X> {
private Class<X> clazz;
if (XIsAnEnum()) {
foo(string, clazz)
}
}
This does not compile because even though I know, from the logic of XIsAnEnum, that X extends Enum<X>, I don't explicitly state this in the generic type parameter definition, so it is not a valid argument.
Is there a way to do an unchecked cast from Class<X> to Class<X extends Enum<X>>, or will I have to make a new class bar2<X extends Enum<X>> specifically for when I want to use enums?
You can use Class#asSubclass(Class) to do the cast for you, like
foo("value", clazz.asSubclass(Enum.class));
This involves an actual verification that clazz is referring to a Class that is a subclass of Enum.
You're throwing out all the generic verification here though.

Generics: Compiler seems incapable of recognizing that the type passed to an argument is the same as what is returned - why?

Let's say I have several POJOs which all extend a common supertype, BaseObject.
I have a GenericDao which is declared as public interface GenericDao<T>.
For each type-specific DAO, I have an interface which extends the generic type and restricts it to a concrete type (public interface UserDao extends GenericDao<User>) and then an implementation of the type-specific DAO.
In a class that attempts to use a number of GenericDao implementations, I have a method that looks like
public <T extends BaseObject> long create(T object) {
return getDao(object.getClass()).save(object);
}
If I implement getDao() so that it's parameter is a Class object, such as
private <T extends BaseObject> GenericDao<T> getDao(Class<T> clazz) { ... }
Then the call to getDao(object.getClass() in the create() method fails to compile - the compiler appears to interpret the return type of getDao() as
GenericDao<? extends BaseContractObject>
rather than recognizing that getDao(Class<T>) is going to return me a GenericDao of the same type T.
Can someone explain why this is? I understand that repeated appearances of the same type bound or wildcard don't necessary refer to the same type; however it seems like the compiler should recognize from the signature of getDao(Class<T>) that the T passed in should be the same T returned (but obviously it isn't capable of recognizing this, the why is the part I fail to grasp).
If I instead define getDao's signature to be
private <T extends BaseContractObject> GenericDao<T> getDao(T obj) { ... }
Then there is no issue in compiling a create() implementation which looks like
public <T extends BaseContractObject> long create(T object) {
return getDao(object).save(object);
}
So why is the compiler capable of recognizing in this case that the T argument passed to getDao(T) is the same T in the return type, whereas it couldn't recognize this when the argument was Class<T>?
The expression object.getClass(), where object is of type T extends BaseObject, returns a Class<? extends BaseObject>, not a Class<T> as one might expect. So, getDao() is returning a DAO of the same type it receives; it's just not receiving the expected type.
This is a classic type erasure issue. getClass() has the following signature:
public final native Class<? extends Object> getClass();
If you have a String and do a getClass() on it, the class you get is Class<? extends String>. The javadocs read:
* #return The <code>java.lang.Class</code> object that represents
* the runtime class of the object. The result is of type
* {#code Class<? extends X>} where X is the
* erasure of the static type of the expression on which
* <code>getClass</code> is called.
You will need to force the following cast to get it to work:
#SuppressWarnings("unchecked")
Class<T> clazz = (Class<T>)object.getClass();
return getDao(clazz).save(object);
That works for me.
I think this should explain why the constraint is not doing what you expect:
http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeParameters.html#FAQ206

Categories

Resources