tags:

views:

133

answers:

5

I have two very simple classes, one extends the other:

public class LocationType implements Parcelable {
    protected int    locid = -1;
    protected String desc  = "";
    protected String dir   = "";
    protected double lat   = -1000;
    protected double lng   = -1000;

    public LocationType() {}

    public int getLocid() {
        return locid;
    }

    public void setLocid(int value) {
        this.locid = value;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String value) {
        this.desc = value;
    }

    public String getDir() {
        return dir;
    }

    public void setDir(String value) {
        this.dir = value;
    }

    public double getLat() {
        return lat;
    }

    public void setLat(double value) {
        this.lat = value;
    }

    public double getLng() {
        return lng;
    }

    public void setLng(double value) {
        this.lng = value;
    }



    // **********************************************
    //  for implementing Parcelable
    // **********************************************

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt   (locid);
        dest.writeString(desc );
        dest.writeString(dir  );
        dest.writeDouble(lat  );
        dest.writeDouble(lng  );
    }

    public static final Parcelable.Creator<LocationType> CREATOR = new Parcelable.Creator<LocationType>() {
        public LocationType createFromParcel(Parcel in) {
            return new LocationType(in);
        }

        public LocationType[] newArray(int size) {
            return new LocationType[size];
        }
    };

    private LocationType(Parcel dest) {
        locid = dest.readInt   ();
        desc  = dest.readString();
        dir   = dest.readString();
        lat   = dest.readDouble();
        lng   = dest.readDouble();
    }
}

and:

public class MyLocationType extends LocationType {
    private ArrayList<ArrivalType> mArrivals = new ArrayList<ArrivalType>();

    public List<ArrivalType> getArrivals() {
        return mArrivals;
    }

    public void addArrival(ArrivalType arrival) {
        mArrivals.add(arrival);
    }
}

The problem is that when I cast an instance of LocationType to MyLocationType I get a ClassCastException. Why is this?

+5  A: 

Because LocationType is the superclass; it can't be cast to the subclass.

To explain a bit further: you can only cast up the inheritance tree, which is to say, an object can only be cast as the class type it was created as, any of its superclasses, or any interface that it implements. Thus, a String can be cast as either a String or an Object; a HashMap can be cast as a HashMap, an AbstractMap Map, or an Object.

In your case, a MyLocationType can be either a MyLocationType or a LocationType (or an Object), but not the other way around.

The Java docs on inheritance are pretty good, just to review here.

delfuego
+1  A: 

This is because LocationType is not an instance of MyLocationType it is it's parent. For example if you added new methods to MyLocationType not in LocationType and then cast LocationType to MyLocationType what would expect to happen if these new methods were called that do not exist in the base LocationType?

MyLocationType is a LocationType but LocationType is not a MyLocationType.

Jeremy Raymond
+1  A: 

Because MyLocationType is of type LocationType and can be cast to one, but LocationType is not of MyLocationType and therefor cannot be cast to it.

Tamar
A: 

Because the reference you're trying to case is not an instance of MyLocationType

This sample always help me to think what's happening.

  Object o = new Object();

  String s = ( String ) o;

Although String inherits from Object that doesn't mean that every Object is an string ( or in this case, that the "casted" object is an String )

OscarRyz
+1  A: 
LocationType lt = new LocationType();
MyLocationType myLt = new MyLocationType();

LocationType t1 = (LocationType)lt; // OK, but cast not required
LocationType t2 = (LocationType)myLt; // OK, but cast ALSO not required
MyLocationType t3 = (MyLocationType)lt; 

// ClassCastException, MyLocationType extends (is-a) LocationType, 
// not the other way around
MyLocationType t3 = (MyLocationType)lt; 

MyLocationType t5 = (MyLocationType)myLt; // OK, but cast not nessecary

explicit casting down the tree is almost never required, and up the tree is risky:

public void doThis(LocationType lt) {
   MyLocationType myLt = (MyLocationType)lt; // DANGEROUS
}

this kind of stuff should only be done if you override a method with a certain signature, and even then you should check:

@Override
public void doThis(LocationType lt) {
   if (lt instanceof MyLocationType) {
     MyLocationType myLt = (MyLocationType)lt; 
   } else {
     // A LocationType but not MyLocationType
     doSomethingElse(lt);
   }
}
extraneon