Solace
Solace

Reputation: 9020

Superclass can not be cast to subclass in one example, while casts smoothly in another example. Why?

Edit # 1 add at the bottom of the question.


I got the ClassCastException in an Android project, but the problem seemed to be in the domain of Java, so I wrote the following SSCCE. In this SSCCE, I get the following exception on 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)

As you can see in the code given, we are trying to cast a RecyclerView.ViewHolder to ParentViewHolder (which is actually a subclass of RecyclerView.ViewHolder)


Although I do understand that Superclass can not be cast to subclass, but my confusion is the following:

The Android project that I am working on actually uses this ExpandableRecyclerView implementation as a library project. In this project, the same thing happening does not cast any problems. That is see Line # 146 on in this class in which RecyclerView.ViewHolder is being cast to ParentViewHolder which, you can see on Line#19 here, extends 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); 
        }
    }
}

_____________________________________________________________________________

EDIT 1:

Another piece of information that may be important is that according to my needs, I made a tiny change to ExpandableRecyclerAdapter:

  1. Line # 119 and Line # 123 are method calls in the method body of onCreateViewHolder method which receives an argument int viewType. I added this viewType argument to the calls to onCreateParentViewHolder and onCreateChildViewHolder on Line#119 and Line#123.

  2. In accordance with ^, I add the int viewType arguments to the corresponding method declarations on Line#187 and Line#195

Upvotes: 1

Views: 409

Answers (1)

fdreger
fdreger

Reputation: 12505

I think you have a wrong mental picture of what "casting" is - you seem to imagine that casting changes class of an object. This never happens!

Casting only allows us to reveal the true type of an object that is referenced by a variable of superclass type. This is sometimes necessary, because in Java both references and objects have types, and they do not have to exactly match.

In this example mySuperRef is a reference of type MySuper, but it really points to an object of type MySub. That's why we can cast it to reveal the true type:

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

In this example, the same cast will give as an error, because the "true type" of an object referenced by mySuperRef is different:

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

To wrap it up: in Java you can only cast an object to a type that the object already has. Casting does not allow us to modify objects in any way, it is only used to assign the object to it's proper reference variable.

Upvotes: 2

Related Questions