Polymorphism And Inheritance

Loosely speaking, in order to have polymorphism we need to have some kind of inheritance.

More exactly, polymorphism (InternalPolymorphism) manifests when we invoke a function (dispatch a message) on a reference to an object.

In order for the polymorphic invocation to work, the reference must point to an object whose actual type (or class) must inherit from a base class (type/interface) that the client code has knowledge of. Thus polymorphism is inherently related to inheritance (understood in the larger sense of a IS-A relation between objects and types ).

There is a little bit of inherent difficulty in addressing the subject generated by the fact that current OO programming languages don't treat the IS-A relation very loosely (class/interface/types) or don't support it quite well, at least IMO. -- CostinCozianu

Rebuttals:

In Smalltalk polymorphism is orthogonal to inheritance.

In Java we have interfaces, and implementing an interface is essentially different than extending a base class (which is the usual meaning of inheritance).

Any language that supports reflection also does not require that polymorphism is related to inheritance.


See OnUnderstandingTypes, ConfusionAboutInheritance, WhatIsDelegation


Check out http://soronthar.blog-city.com/read/175488.htm

-- RafaelAlvarez


What I mean by inheritance is actually subtyping, inheriting some featuresand/or constraints from a base type (class/interface/AbstractDataType). More precisely, the object is involved in an IS-A relation with a specific base type that the client knows of.

You polymorphically invoke a function through an object reference either directly using the OO language facility, or through reflection. Now we have to distinguish between statically-typed/dynamic languages (C++ versus Smalltalk), and invocation through reflection.

Case A: Statically typed languages (C++, Java). Here the things are clear, you cannot make an invocation through an reference unless that reference has a specific type, and if the language is type consistent the reference cannot point to an arbitrary object, but only to an object that conforms to an base class definition (eventually an interface in Java). Anyway, the result is that the actual object is in a IS-A relation with the base class (either abstract or concrete) or interface in Java.

Case B: Dynamic languages like Smalltalk. Here it is obvious that the actual class of an object need not inherit from a specific base class for the polymorphic invocation to happen. However, even if it's not something directly supported by the language we still witness a hidden (implicit) IS-A relation as is the case with invocation through reflection detailed below.

Case C: Invocation through reflection. Again it is obvious that the target object's actual class need not inherit (as in specific OO language defined inheritance) from a base class, or implement a specific interface.

Let's focus on an example that's well known: JavaBeans. The typical client of JavaBean invocation through reflection is a Java IDE who gets the actual class of the object, inspect it to find the methods who match the getXXX()/setXXX()/isXXX() pattern (we skip the programatic interfaces defined in java.beans.*) and may invoke those methods dynamically (I don't know if polymorphically is the right word here) upon user triggered action in the property inspector. So apparently there is no IS-A relationships in this case.

But what happens here is that there is a external contract (not enforced by the programming language compiler) between the client of a JavaBean and the JavaBean object, the contract defined in the JavaBeans specification, the only problem is that the specific language (Java in this case) is not flexible enough to be able to directly support/enforce this type of contract. Therefore the object IS-A JavaBean, and since the notion of the type is generally understood as a set of values, we have that JavaBean is really a type (although not supported by the language), and a well programmed JavaBean, even has to conform to LiskovSubstitutionPrinciple, that is it has to work as expected in any IDE/runtime that is programmed to be the client of the JavaBean contract as defined in an external specification (not enforceable by the language).

The similar case can be made with a dynamic language: when the programmer invokes a method on a reference, he really expects that reference to conform to a more or less implicit contract . If you invoke a x.method_y() the object x is at least expected to be in an IS-A relation with a specific type. Even if that type is defined as loosely as all objects whose class have a method named method_y and takes no parameter and returns void.

Sometimes the reflective invocation, can be made by a scripting engine who simply reads a string from the script and in interpreting it, generates the dynamic call. Here the contract moves one level above and is established between the object targeted and the script code that generates the method invocation, thus the contract is a little bit cross-language, but we still have an IS-A relation.


<Raw thread>

When we say that we want to store polymorphic relations (or collections, or better said sets) in the database, we always talk about a base type that all objects in the polymorphic relation should conform to -- CostinCozianu

I don't believe this is always true. It certainly isn't necessarily for multiple inheritance languages like Eiffel or C++. It is very inconvenient for Java and Smalltalk. -- AnonymousDonor

And for most OO approaches inheritance is primarily related to code reuse - we want to reuse code from the base class, and usually implies that the class that inherits from the base class has more attributes but not less (of course the code from the base class still has to access its needed attributes).

Inheritance is only one small part of reuse. In the alarm clock example, no inheritance is necessary to reuse the alarm clock for every object in the system. Polymorphism is the key in this example.

And you can't have polymorphism without inheritance. Java interfaces a re just a disguised application of multiple inheritance, where the mess of dealing with inheritance of instance attributes is eliminated. However, Java implements Interface is structurally the same as inheriting from an abstract base class in C++. The fact that you are able to invoke a specific method on a reference is supported by the fact that that reference IS-A (conforms to) specific type. The generalization of inheritance with all its twists in the current programming languages is the IS-A relation.-- Costin

Untrue. Smalltalk's polymorphism is orthogonal to inheritance. In a sense, Java's is too since polymorphic behavior can be implemented through reflection and, often, very usefully is.

I don't think it's a good idea to link polymorphism and inheritance. I find polymorphism to be very valuable; inheritance, on the other hand, seems to introduce as many problems as it solves. With creative definitions of types, you can say that all polymorphism is linked to inheritance, but again, I don't think it's a good idea. I'd like to see more languages that implement polymorphism and not inheritance (and use some other technique to provide implementation reuse). -- JimLittle

</raw thread>

As I am unqualified to speak on Smalltalk, I will accept that polymorphism can be done without inheritance (although I would be interested in hearing the details). I would suggest, however, the only purpose for using inheritance is to provide polymorphism. I would also say that using inheritance merely to reuse code is an incorrect use, leading to clutter and confusion. -- WayneMack

In Smalltalk (and Self and Ruby and Python and...), you can send any message to any object you want. The compiler doesn't check any types at compile time. At runtime, when an object receives a message, it checks to see if it has a method with the same name as the message. If it finds one, it executes the method; if it doesn't, it throws a "message not understood" exception.

The point (for this page) is that it doesn't matter what class an object is, as long as it responds to the messages you send it. That's what Smalltalkers mean by "polymorphism" - you can substitute any object for any other object, as long as it responds sensibly to all the messages you're going to send it.

(Lots of advantages and disadvantages to all this, of course. There are plenty of other pages on this wiki for that. But this is how languages like Smalltalk and Python do it. See TimLesher's example down there.)

In these languages, inheritance really is just for code reuse. You don't need it for polymorphism - if you want A to respond to the same messages as B, but you don't want to reuse B's implementation, there's no reason to have A inherit from B.

-- AdamSpitz

Why would you use inheritance for code reuse? I still don't see any rationale for inheritance if an alternative mechanism provides inheritance. Would some care to expand upon this for me? -- WayneMack

Suppose that I want to make an Integer class and a Float class. At first I make them both subclasses of Object. Later on, I notice that Integer and Float have a lot of code in common. For example, they both have an identical "squared" method:

        def squared
          return self * self
        end
So I make a class called Number, make Integer and Float both be subclasses of Number, and put the "squared" method into the Number class. Now that code isn't duplicated anymore. Code reuse. (And polymorphism too.) You already understand this.

Inheritance still gives you polymorphism. But you only need to use it in the cases where you want polymorphism and code reuse. Languages like Java force you to inherit from little "interfaces" even when you just want polymorphism. (See the SeekableAndWritable example below for an attempt to explain why this can cause problems.)

-- AdamSpitz


Another method to get polymorphism without inheritance is DuckTyping. DuckTyping can be used in (at least) C++ (via the STL) and Python. For example, say that (in Python) you have a File class:

 class File:
   def __init__(self):
     ...
   def read(self, numBytes):
     ...
   def write(self, data):
     ...
   def seek(self, offset):
     ...
And you have a number of functions that can act on files:

 def WriteAOne(someFile):
   someFile.seek(0)
   someFile.write("1")
You can use WriteAOne polymorphically with any object that implements the methods WriteAOne calls. -- TimLesher

This is the same as Java's interface is it not? -- PeteHardie

In Java, you would need the creator of the File class to anticipate your needs and create an explicit SeekableAndWritable interface and mark File as implementing it. In Smalltalk or Python, you just create a new object that understands the "seek" and "write" methods and everything'll work. -- AdamSpitz

Could someone show the above example in C++? I appraently missed the "aha" in the explanation.

In C++, the WriteAOne function would look something like this:

        void WriteAOne(File* someFile) {
           someFile->seek(0);
           someFile->write("1");
        }
... which is pretty much identical to the Python version, except that I had to write "File*" in the signature. That's probably a bad idea, because I want to be able to WriteAOne to any object that has "seek" and "write" methods, not just Files. So to get that amount of polymorphism, I need there to exist an "interface" like this:

        class SeekableAndWritable {
        public:
          virtual void seek(int i) = 0;
          virtual void write(char* s) = 0;
        };
... so that I can write the WriteAOne function like this:

        void WriteAOne(SeekableAndWritable* someFile) {
           someFile->seek(0);
           someFile->write("1");
        }
The problem is, I need the File class to inherit from SeekableAndWritable. If I'm not the one who created the File class, then I'm stuck - I won't be able to use a File as a SeekableAndWritable.

(Of course, C++ has templates and stuff, so I'd have alternatives. In Java I really would be stuck.)

In a language with DynamicTyping (like Smalltalk or Python or Ruby), these problems just disappear. You don't need to create explicit "interfaces". You don't need the creators of your library classes to anticipate all the little interfaces you'll need. You don't need the creator of the WriteAOne function to remember to declare the parameter as SeekableAndWritable instead of File. You just make sure that you pass in an object that understands all the messages that'll be sent to it, and everything works.

-- AdamSpitz


What about Parametric Polymorphism using only parameters to procedures and functions? See IncludeFileParametricPolymorphism. This doesn't require inheritance.

ParametricPolymorphism (one piece of code statically dispatching to many possible locations based on parameter types) is not quite the same as OO 'virtual' method Polymorphism (one piece of code doing a runtime lookup for a method based upon a special runtime argument, that being the 'this' object pointer). Both are less generic than GenericMethod?s which allow for true MultipleDispatch (another BlubParadox thing, and yet another case of Lisp getting it right thirty years earlier). All forms of polymorphism suffer from issues of ambiguity in the case where some types can be viewed as or coerced into other types, and not all types match exactly.

In IncludeFileParametricPolymorphism, the "not all types match exactly" problem is solved by using compile time IFDEF directives. It'd be better if it was implemented in the language directly, though.


See InternalPolymorphism, CircleAndEllipseProblem, LiskovSubstitutionPrinciple.


CategoryPolymorphism CategoryObjectOrientation


EditText of this page (last edited May 22, 2008) or FindPage with title or text search