【问题标题】:Cannot convert from not generic subclass to generic superclass无法从非泛型子类转换为泛型超类
【发布时间】:2013-08-10 04:33:07
【问题描述】:

我正在尝试根据 T 的类型获取接口实例。扩展 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>{}

我明白了

Type mismatch: cannot convert from PlaceDataSource to IDictionaryDataSource<T>

Type safety: Unchecked cast from PlaceDataSource to IDictionaryDataSource<T>

如果我像上面那样投射它。

你能解释一下为什么会出现编译错误和警告吗?

这里会调用它

public static <T extends BaseDictionary> DictionaryElementPickerFragment<T> newInstance(Class<T> clazz, Context cxt){
     //somecode here
     fragment.setDataSource(DictUtils.getEntityDataSourceInstance(clazz, cxt));
}

我试图在这里和谷歌中找到答案,但没有成功。我将不胜感激。 现在我是这样想的

没有解决此问题的辅助方法,因为代码根本上是错误的。

提前致谢。

【问题讨论】:

    标签: java


    【解决方案1】:

    这个更具体的例子说明了你的问题,这是类型参数变化之一。

    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
    }
    

    所以List&lt;String&gt; 不是List&lt;Object&gt;,尽管它是List&lt;? extends Object&gt;


    在您的特定情况下,您不能投射

    PlaceDataSourceIDictionaryDataSource&lt;T&gt;

    PlaceDataSource 是一个 IDictionaryDataSource&lt;Place&gt;,但我们对&lt;T&gt;的唯一了解是它扩展了BaseDictionary,它是BaseDictionary的超类。

    因此您可以将PlaceDataSource 投射到

    1. IDictionaryDataSource&lt;Place&gt; 或到
    2. IDictionaryDataSource&lt;? super Place&gt; 或到
    3. IDictionaryDataSource&lt;? extends BaseDictionary&gt;

    但不是IDictionaryDataSource&lt;T&gt;,因为T不能保证是Place,这样做会导致实际类型参数Place和形式类型参数T不匹配。

    【讨论】:

      【解决方案2】:

      这不是安全的实施方式,而且很危险,因为(Place.class.isAssignableFrom(clazz) 将您保存在这里):

      T 是泛型类型,您将其替换为明确的Place

      如果我有怎么办

      public class Home extends BaseDictionary{}
      public class HomeDataSource extends BaseDictionaryDataSource<Home>{}
      

      然后我用Home 类调用getEntityDataSourceInstance,但得到PlaceDataSource,它不能转换为我期望的HomeDataSource (IDictionaryDataSource)。所以我最终会得到ClassCastException

      【讨论】:

      • so.. 我怎样才能根据什么类变量来实现获取某些数据源?或者我应该重写哪些部分?
      • 无论如何你都会收到这个警告。但是Place.class.isAssignableFrom(clazz) 将您保存在这里,以便您没有上述情况。可以使用@suppressWarnings注解
      • 嗯,也许我应该使用一些魔法模式或我不知道的东西?
      • 三思而后行是否需要泛型方法。也许你需要 1 或 2 个类,然后实现它们。其他解决方案是使用Factory Pattern。一个很好的例子在这里stackoverflow.com/questions/855332/…
      【解决方案3】:

      看起来getEntityDataSourceInstance 应该是BaseDictionary 类中的实例方法,而不是静态方法。

      子类将知道要创建哪种类型的DictionaryDataSource

      【讨论】:

      • BaseDictionary 只是一个带有 getter 和 setter 的模型,我被告知不要在那里这样做
      【解决方案4】:

      试试@SuppressWarnings("unchecked")

      【讨论】:

      • 没有人谈论错误。 a_vasilkov 的代码没有错误。泛型在 Java 中的实现非常糟糕,通常它是抑制这些关于未经检查的强制转换的讨厌警告的唯一解决方案。
      • 错了,弗兰克。如果您没有任何不安全类型警告,则在运行时您将永远不会有任何ClassCastException。如果您不关心这一点,只需使用原始类型。但是一半时间使用泛型并忽略您收到的有关实际问题的警告(在这种情况下;代码确实有错误)是没有意义的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-02
      • 1970-01-01
      • 1970-01-01
      • 2012-04-21
      相关资源
      最近更新 更多