【问题标题】:Could not downcast using List class in Java [duplicate]无法在 Java 中使用 List 类进行向下转换
【发布时间】:2013-03-03 21:14:50
【问题描述】:

我一直在寻找答案,但无济于事。我的问题是为什么不可能对泛型感到沮丧。我有一个名为 Job 的类并扩展了一个名为 Model 的类

Job extends Model

现在我从生成模型列表的可重用代码中获取作业集合

// error: Cannot cast from List<Model> to List<Job>
List<Job> jobs = (List<Job>) jobMapper.fetchAll();

其中 jobMapper.fetchAll() 返回一个 List,其中每个模型都是一个 Job 对象。

我认为这会起作用,因为我可以做到:

EditText mUsername = (EditText) findViewById(R.id.editUserName);

这是一个简单的向下转换。

【问题讨论】:

  • Job 是 Model 的子类,Job[] 是 Model[] 的子类,但 List 不是 List 的子类。
  • @JermaineXu:这里的“子类型”比“子类”更正确。因为List&lt;Job&gt; 并不是真正的类,而是一种类型。 (你可以争辩Job[]也不是一个类)。
  • 没错,感谢您的指正。 @JoachimSauer

标签: java generics collections downcast


【解决方案1】:

你不能这样做,因为 Java 不允许这样做。 Read this.你应该做的伎俩:

 List<Job> jobs = (List<Job>) ((List<?>)jobMapper.fetchAll());

【讨论】:

  • 太棒了!那成功了。虽然我收到未经检查的演员表警告,但我可以忍受,因为我很确定要转换的值。谢谢你的帮助。非常感谢!
  • 顺便说一句:这是一个非常坏主意。它只会隐藏警告并有效地从此列表中删除所有泛型类型信息(以及您从泛型获得的所有保证)。
  • @Joachim 有没有更好的方法来做到这一点?你能发表一个答案吗?谢谢
  • @JonasTandinco:更好的方法是在需要List&lt;Model&gt; 时返回List&lt;Model&gt;。类型系统设计不允许将List&lt;Job&gt; 转换为List&lt;Model&gt;,因为它无法检查转换在运行时是否有效(即它是“未经检查的转换”!)。允许使用泛型代码编译遗留(预泛型)代码。
  • +1 到@JoachimSauer。如果 Java 告诉您它不想做某事,则意味着您做错了事,如果可能,应该重新设计您的代码。
【解决方案2】:

您可以执行以下操作:

List<Job> jobs = (List) (jobMapper.fetchAll());

(如果您确信它在您的情况下是安全的,则抑制警告

编译器不允许您尝试进行强制转换,因为一旦您有一个 List 和一个 List 指向同一个列表,您可以将 Model 实例添加到后者,并使 List里面有一个模型项,这破坏了类型安全。

因此,在允许这种技巧时要小心 - 它可能会在稍后以 ClassCastException 的形式返回给您,而您不会期望它发生。

关于您的最后一个问题:请注意,虽然 Job is a Model 并且 Job[] is a Model[],但集合并非如此:List不是 List。这有点令人惊讶,但它遵循我上面的解释。在没有警告/错误的情况下允许这种转换会破坏类型安全。

【讨论】:

    【解决方案3】:
    List<Job> jobs = jobMapper.fetchAll();
    

    错了,它从来都不是明确的工作列表。

    使用

    List<? super Job> jobs = jobMapper.fetchAll();
    

    改为。

    【讨论】:

    • 这只允许将作业作为对象进行迭代(并将它们转换为作业)。
    • 因为它从来不是一个明确的工作列表。 :D
    • 很抱歉,这对我不起作用。而该行仅发出警告。我不能再使用作业变量,因为我在数组适配器 ArrayAdapter 中使用它,并且不能再将此集合添加到适配器,因为它不再将其识别为 List
    • 因为您不能确保 fetchAll 始终返回作业列表。使用这种逻辑,您可以将 java.lang.System 转换为 Cloneable 并克隆它以获得全新的 PC。
    • 好的,所以你给我一个-1,因为我的解决方案可能无法工作,具体取决于jobMapper 的实际类型/类层次结构,而是给你一个解决方案已经知道 永远 会解决 OP 的问题吗?嗯....
    【解决方案4】:

    这是不允许的,因为它会/可能导致运行时错误。如果列表已经包含不属于Job 类的对象怎么办?

    您应该:

    1. jobMapper.fetchAll() 更改为返回List&lt;Job&gt;

    2. 投射对象而不是列表,即。 Job job = (Job) jobs.get(0).

    【讨论】:

    • 以类似的方式,在 findViewById() 示例中将 View 转换为 EditText 可能会导致 RuntimeExceptions.. 但它仍然是允许的。
    • 我不能让 jobMapper.fetchAll() 返回 List 因为 fetchAll 是我正在重用的基类的方法。我也在做 userMapper.fetchAll() 它返回一个用户对象(用户扩展模型)。
    • @baske:对 EditText 的强制转换会强制转换一个对象。当你做这样的演员时,你必须期待一个 ClassCastException 。但是,当从通用容器中检索对象时,您不会强制转换返回的元素,因此您永远不会得到 ClassCastException(除非您强制转换集合并忽略所有警告 - 请参阅 Joachim Sauer 对 Leonidas 的解决方案的评论)。
    • @PeterRader:是的,但这只是意味着 OP 没有提供足够的信息。最大的区别是,当使用(Job) jobs.get(0) 时,您将在您的演员现场获得 ClassCastException。转换列表时,在尝试从列表中实际检索项目(编译器隐式将转换添加到 Job)之前,您不会得到 ClassCastException(因为编译器丢弃了泛型类型信息)。这可能与您投射列表的代码相距甚远,并且可能难以调试。
    • @Axel:对 EditText 的强制转换是一个视图,而不是一个对象。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-09-13
    • 1970-01-01
    • 2015-04-13
    • 1970-01-01
    • 2016-02-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多