views:

184

answers:

6

I have a superclass that handles car decal information. I have a subclass that handles specific decal information for a transaction. I have a special override in the transaction decal that is used to check for certain things before a decal # can be set. The problem I'm having is that sometimes I need to grab information about a generic decal object and set it to my transaction decal. For instance:

TransactionDecal myTransactionDecal = new TransactionDecal();
Decal myGenericDecal = new Decal();
myTransactionDecal = (TransactionDecal) myGenericDecal.getGenericDecal();

But I get a runtime error telling me I can't cast between the types. What exactly am I doing wrong, and is this the correct way to go about it? Thanks!

+3  A: 

The principle of substitutability would indicate that any type can be substibuted by itself or a subtype.

Here, you're doing the reverse - you're attempting to reference an instance of the supertype in a variable of the subtype (assuming Decal is the superclass, and TransactionDecal the subclass).

This is blocked, because inheritance exists to specialise an existing class, i.e. to extend the interface; in general, substituting a superclass would limit the interface, and this would allow calls to methods (via the referencing variable type) that are not implemented in the superclass.

Jeremy Smyth
Ah, I get what you're saying. So what would be the solution? Should I make an Adapter that just takes the values of the super class and sets them to the values of the sub class. That doesn't seem very practical, though. There has to be some way to do this.
Austin
Are the parent object's attributes visible to the subclass, either public or via mutator methods? Or perhaps do you need to _read_ static values from the parent? I'm not sure why you can't access those values from the subclass anyway, without needing a new object?One other possibilty would be to offload the business rules into a "Transaction" instance, and give it a "has-a" relationship (aggregation) with a specific Decal instance.
Jeremy Smyth
Jeremy, please read my comment on JP's post. I think I've figured out I really have no need to be casting here.
Austin
Thanks, all but Jeremy's solution made me realize what I was doing wrong. Since he said casting should only change the value of a variable, I got to thinking about my Clone method I was using. When I was cloning, I was creating a new decal, setting its properties to the current instance, then trying to cast it to the transactionDecal. That won't work because I said in the clone method "New Decal()." Instead I made a copy method that takes any kind of decal as a parameter and sets its props. That way I still can use its special setter since I'm not casting to a base type.
Austin
+1  A: 

You can't do this because you would cast the less specialised class (Decal) to a more specialsed class (TransactionDecal). That can't work as now you would be able to call methods on Decal which aren't implemented there (just think of maybe newly introduced variables in TransactionDecal which are used by the new methods or the overriden methods) they can't possibly exist in an instance of Decal.

jitter
A: 

Jeremy's explanation of why this doesn't work is on the money.

If you want to get around this issue, you can make transactionDecal not inherit from Decal but instead contain an instance of Decal. That way you can set the internal instance to be the generic decal without losing any transaction data.

There are of course other patterns, but this is one of the more popular patterns for handling this kind of issue.

patros
A: 

This is called downcasting. This is a pretty good quick intro of when it's okay and when it's not (in C# anyway) ...

Animal a;
a = b;
Bird b = (Bird) a; // Okay

This code will compile successfully. At run time, the cast operator performs a check to determine whether the object referred to is really of type Bird. If it is not, the run-time InvalidCastException is raised.

But, you shouldn't need to do it. Your derived type should be able to do everything your base type can do. If you have a base type and want something that only a derived type can do, then you better get an instance of a derived type to do it! :)

Edit per comment: There is no reason you can't provide a Derived constructor that takes an instance of a Base and builds a Derived around that (like a default Derived around a specific Base) ...

public D(B b) { }

But, it sounds to me like you need an interface or you should relax the requirements on your method parameter. After all if your method can operate on an Animal just fine, there is no reason it should force you to hand it a Bird which is what your method seems to be doing.

JP Alioto
Thanks. That just made me think of something--really the only reason I even need to cast is because I need the actual property values of the base. I stil want to retain all of my specialized method calls on the subclass, so I really don't think I should be casting :) Would a clone method work better here?
Austin
A: 

Using your example here's how Polymorphism can be implemented:

// cast derived object as base object
Decal myGenericDecal = null;
TransactionDecal myTransactionDecal = TransactionDecal.GetTransactionDecal()
myGenericDecal = myTransactionDecal;

// cast base object to a derived object
TransactionalDecal newTransactionalDecal = null;
newTransactionalDecal = (TransactionalDecal)myGenericDecal;
shizbiz
A: 

It depends on what you want to do with myTransactionDecal, especially whether you need TransactionDecal's data members. How about adding a constructor?

TransactionDecal::TransactionDecal(const Decal &);

Or maybe just move the functionality you want into Decal, or into a non-member function?