【问题标题】:Superclass can not be cast to subclass in one example, while casts smoothly in another example. Why?在一个示例中,超类不能转换为子类,而在另一个示例中可以平滑转换。为什么?
【发布时间】:2015-07-26 06:03:26
【问题描述】:

Edit #1在问题底部添加。


我在一个Android项目中得到了ClassCastException,但是问题似乎出在Java领域,所以我写了以下SSCCE。在这个 SSCCE 中,我在 PVC parentViewHolder = (PVC) holder; 上收到以下异常:

Exception in thread "main" Overridden onBindViewHolder in my ExpandableRecyclerAdapter called.
java.lang.ClassCastException: practice_programs.inheritance.class_cast_to_super.RecyclerView$ViewHolder cannot be cast to practice_programs.inheritance.class_cast_to_super.ParentViewHolder
    at practice_programs.inheritance.class_cast_to_super.ExpandableRecyclerAdapter.onBindViewHolder(ExpandableRecyclerAdapter.java:9)
    at practice_programs.inheritance.class_cast_to_super.ExpandableRecyclerAdapter.main(ExpandableRecyclerAdapter.java:15)

正如您在给出的代码中看到的,我们正在尝试将 RecyclerView.ViewHolder 强制转换为 ParentViewHolder(实际上是 RecyclerView.ViewHolder 的子类)


虽然我知道超类不能转换为子类,但我的困惑如下:

我正在处理的 Android 项目实际上使用 this ExpandableRecyclerView 实现作为库项目。 在这个项目中,发生同样的事情不会产生任何问题。 参见Line # 146 on in this class,其中RecyclerView.ViewHolder 被转换为ParentViewHolderyou can see on Line#19 here 扩展RecyclerView.ViewHolder


SSCCE:

public class ExpandableRecyclerAdapter<PVC extends ParentViewHolder> extends RecyclerView.Adapter{

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        System.out.println("Overridden onBindViewHolder in my ExpandableRecyclerAdapter called.");
        PVC parentViewHolder = (PVC) holder;//EXCEPTION***************************************
        super.onBindViewHolder(parentViewHolder, position);
    }

    public static void main (String [] args) {
        new ExpandableRecyclerAdapter().onBindViewHolder(new RecyclerView.ViewHolder(), 1);
    }

}

ParentViewHolder.java

public class ParentViewHolder extends RecyclerView.ViewHolder {

    String string = "XAR!";

}

RecyclerView.java

public class RecyclerView {

    public static class ViewHolder {}

    public static class Adapter {
        public void onBindViewHolder(ViewHolder viewHolder, int position) {
            System.out.println("onBindViewHolder of RecyclerView.ViewHolder called."); 
            System.out.println("The string of the passed viewHolder is " + ((ParentViewHolder) viewHolder).string); 
        }
    }
}

_______________________________________________________________________________

编辑 1:

另外一条可能很重要的信息是,根据我的需要,我对ExpandableRecyclerAdapter做了一点小小的改动:

  1. 第 119 行和第 123 行是onCreateViewHolder 方法的方法体中的方法调用,它接收参数int viewType。我在 Line#119 和 Line#123 上对 onCreateParentViewHolderonCreateChildViewHolder 的调用中添加了这个 viewType 参数。

  2. 按照^,我将int viewType参数添加到Line#187和Line#195的对应方法声明中

【问题讨论】:

  • 据我所见,他们的版本在传递错误的类型时应该会导致异常,但我猜想在他们的项目中根本不会发生这种情况,因为要么总是在传递之前检查类型,要么是其他类不变量。
  • @NESPowerGlove 你能看到我的编辑,并告诉我你是否认为我所做的更改会导致问题?
  • 这是两个抽象方法,我不知道它们实现的所有位置,但是在这两个抽象方法的每个实现中添加额外的参数以用作onCreateViewHolder 之类的保护应该只要您传递的守卫与传递的类型匹配,就不会破坏任何我想像的东西。当他们可以只做instanceof 检查时,将 int 枚举作为保护来识别其他参数类型的想法有点奇怪。
  • 我的意思是您检查以确定持有人类型的值,以防止错误地转换持有人类型。
  • 我可以通过我的回答中已经说明的其他方式提供帮助吗?您的文字(粗体,关于铸造)是错误的 - 铸造是可能的。问题是由于在适配器视图中放置了错误类的对象引起的,唯一可以给出的建议(除非您告诉我们您要完成的工作)是 - 不要将对象转换为它们不具有的类型.

标签: java android inheritance casting classcastexception


【解决方案1】:

我认为你对什么是“铸造”有一个错误的想法——你似乎认为铸造改变了一个对象的类别。这永远不会发生!

强制转换只允许我们揭示被超类类型变量引用的对象的真实类型。这有时是必要的,因为在 Java 中,引用和对象都有类型,它们不必完全匹配。

在此示例中,mySuperRefMySuper 类型的引用,但它实际上指向 MySub 类型的对象。这就是为什么我们可以强制转换它以显示真实类型:

MySuper mySuperRef = new MySub();
MySub mySubRef = (MySub)mySuperRef;

在这个例子中,同样的转换会报错,因为mySuperRef引用的对象的“真实类型”是不同的:

MySuper mySuperRef = new MySuper();
MySub mySubRef = (MySub)mySuperRef;

总结一下:在 Java 中,您只能将对象转换为该对象已经具有的类型。强制转换不允许我们以任何方式修改对象,它仅用于将对象分配给它的正确引用变量。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-10
    • 2011-12-25
    • 1970-01-01
    • 2017-01-18
    • 2014-01-30
    相关资源
    最近更新 更多