Question

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!

Was it helpful?

Solution

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.

OTHER TIPS

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.

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.

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;

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?

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top