简体   繁体   中英

Cannot convert from not generic subclass to generic superclass

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

  1. an IDictionaryDataSource<Place> or to
  2. an IDictionaryDataSource<? super Place> IDictionaryDataSource<? super Place> or to
  3. an IDictionaryDataSource<? extends BaseDictionary> 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.

尝试@SuppressWarnings(“ unchecked”)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM