Polymorphism in object-oriented programming

From Wikipedia, the free encyclopedia

Jump to: navigation, search

In simple terms, polymorphism is the ability of one type, A, to appear as and be used like another type, B. In strongly typed languages, this usually means that type A somehow derives from type B, or type A implements an interface that represents type B.

In weakly typed languages types are implicitly polymorphic.

Operator Overloading the numerical operators +, -, /, * allow polymorphic treatment of the various numerical types: integer, unsigned integer, float, decimal, etc; each of which have different ranges, bit patterns, and representations. Another common example is the use of the "+" operator which allows similar or polymorphic treatment of numbers (addition), strings (concatenation), and lists (attachment). This is a lesser used feature of polymorphism.

The primary usage of polymorphism in industry (object-oriented programming theory) is the ability of objects belonging to different types to respond to method, field, or property calls of the same name, each one according to an appropriate type-specific behavior. The programmer (and the program) does not have to know the exact type of the object in advance, and so the exact behavior is determined at run time (this is called late binding or dynamic binding).

The different objects involved only need to present a compatible interface to the clients (the calling routines). That is, there must be public or internal methods, fields, events, and properties with the same name and the same parameter sets in all the superclasses, subclasses, and potentially interfaces. In principle, the object types may be unrelated, but since they share a common interface, they are often implemented as subclasses of the same superclass. Though it is not required, it is understood that the different methods will also produce similar results (for example, returning values of the same type).

Polymorphism is not the same as method overloading or method overriding. [1] Polymorphism is only concerned with the application of specific implementations to an interface or a more generic base class. Method overloading refers to methods that have the same name but different signatures inside the same class. Method overriding is where a subclass replaces the implementation of one or more of its parent's methods. Neither method overloading nor method overriding are by themselves implementations of polymorphism. [2]

Contents

[edit] Inheritance with Polymorphism

If a Dog is commanded to speak(), it may emit a bark, while if a Pig is asked to speak(), it may respond with an oink. Both inherit speak() from Animal, but their subclass methods override the methods of the superclass, known as overriding polymorphism. Adding a walk method to Animal would give both Pig and Dog object's the same walk method.

Inheritance combined with polymorphism allows class B to inherit from class A without having to retain all features of class A; it can do some of the things that class A does differently. This means that the same "verb" can result in different actions as appropriate for a specific class. Calling code can issue the same command to their superclass or interface and get appropriately different results from each one.

[edit] Examples

[edit] C#

The ideal implementation of polymorphism in C# is to use interfaces stored in a common class API which has no user code dependencies.

// Assembly: Common Classes
// Namespace: CommonClasses
 
using System;
 
namespace CommonClasses
{
    public interface IAnimal
    {
        string Name
        {
            get;
        }
        string Talk();
    }
}
 
// Assembly: Animals
// Namespace: Animals
 
public class AnimalBase
{
    private string _name;
    AnimalBase(string name)
    {
       _name = name;
    }
    public string Name
    {
       get
       {
          return _name;
       }
    }
}
 
// Assembly: Animals
// Namespace: Animals
 
using System;
using CommonClasses;
 
namespace Animals
{
    public abstract class AnimalBase
    {
        private string _name;
        protected AnimalBase(string name)
        {
            _name = name;
        }
 
        public string Name
        {
            get
            {
                return _name;
            }
        }
    }
    public class Cat : AnimalBase, IAnimal
    {
        public Cat(String name) :
            base(name)
        {
        }
 
        public string Talk()
        {
            return "Meowww!";
        }
    }
    public class Dog : AnimalBase, IAnimal
    {
        public Dog(string name) :
            base(name)
        {
        }
 
        public string Talk()
        {
            return "Arf! Arf!";
        }
    }
}
 
// Assembly: Program
// Namespace: Program
// References and Uses Assemblies: Common Classes, Animals
 
using System;
using System.Collections.Generic;
using CommonClasses;
using Animals;
 
namespace TestWikepediaPolymorphismInOOP
{
    public class TestAnimals
    {
        // prints the following:
        //
        // Missy: Meowww!
        // Mr. Bojangles: Meowww!
        // Lassie: Arf! Arf!
        //
        public static void Main(String[] args)
        {
            List<IAnimal> animals = new List<IAnimal>();
            animals.Add(new Cat("Missy"));
            animals.Add(new Cat("Mr. Bojangles"));
            animals.Add(new Dog("Lassie"));
 
            foreach (IAnimal animal in animals)
            {
                Console.WriteLine(animal.Name + ": " + animal.Talk());
            }
        }
    }
}

The reason this is ideal is because then objects can be used in any assembly, regardless of any circular reference issues present. This design is similar to the Strategy Design Pattern, which usually has read/write properties of type interface. This example has a read only property of type interface with the only way to set the strategy interface being via the constructor. Implementing a base class is not required but is typically useful for code reuse through inheritance.

This example also uses generics, which enable many things, including compile time type safety for collections, and no boxing, unboxing, or casting performance penalties in C#. Combining interfaces, design patterns, and generics creates very flexible, extensible, readable, maintainable, high performance designs. [3]

[edit] Java

interface Animal 
{
    String getName();
    String talk();
}
 
abstract class AnimalBase implements Animal
{
    private final String name;
 
    protected AnimalBase(String name) {
        this.name = name;
    }
 
    public String getName() {
        return name;
    }
}
 
class Cat extends AnimalBase 
{
    public Cat(String name) {
        super(name);
    }
 
    public String talk() {
        return "Meowww!";
    }
}
 
class Dog extends AnimalBase 
{
    public Dog(String name) {
        super(name);
    }
 
    public String talk() {
        return "Arf! Arf!";
    }
}
 
public class TestAnimals
{
    // prints the following:
    //
    // Missy: Meowww!
    // Mr. Bojangles: Meowww!
    // Lassie: Arf! Arf!
    //
    public static void main(String[] args) {
        Animal[] animals = {
            new Cat("Missy"),
            new Cat("Mr. Bojangles"),
            new Dog("Lassie")
        };
 
        for (Animal a : animals) {
            System.out.println(a.getName() + ": " + a.talk());
        }
    }
}

[edit] Python

myString = 'Hello, world!'
myList = [0, 'one', 1, 'two', 3, 'five', 8]
 
print myString[:5]  # prints Hello
print myList[:5]    # prints [0, 'one', 1, 'two', 3]
 
print 'e' in myString   # prints True
print 5 in myList       # prints False

However, the most common examples of polymorphism are found in custom classes. Consider the example below, where two subclasses (Cat and Dog) are derived from an Animal superclass. Two Cat objects and one Dog are instantiated and given names, and then they are gathered in an array "animals" and their "talk" method is called.

class Animal:
    def __init__(self, name):    # Constructor of the class
        self.name = name
 
class Cat(Animal):
    def talk(self):
        return 'Meow!'
 
class Dog(Animal):
    def talk(self):
        return 'Woof! Woof!'
 
animals = [Cat('Missy'), 
           Cat('Mr. Bojangles'), 
           Dog('Lassie')]
 
for animal in animals:
    print animal.name + ': ' + animal.talk()
 
# prints the following:
#
# Missy: Meow!
# Mr. Bojangles: Meow!
# Lassie: Woof! Woof!

Note that Python makes polymorphism particularly easy to write, since the language is dynamically (and implicitly) typed: a name can be bound to objects of any type (or class) without having to explicitly specify the type, and a list holds mixed type (unlike a C array or a Java array, be it generic or not). Note the inevitable trade-off though: a language that generates fewer compile-time errors tends to generate more run-time errors, requiring explicit (unit) testing.

Dynamic language performance is hindered by the extra checks and searches that occur at each call site. Straightforward implementations have to repeatedly search class precedence lists for members and potentially resolve overloads on method argument types each time you execute a particular line of code. In an expression such as o.m(x, y) or x + y, dynamic languages need to check exactly what kind of object o is, what is m bound to for o, what type x is, what type y is, or what "+" means for the actual runtime type of x and y. In a statically typed language (or with enough type hints in the code and type inferencing), you can emit exactly the instructions or runtime function calls that are appropriate at each call site. You can do this because you know from the static types what is needed at compile time.

Dynamic languages provide great productivity enhancements and powerful terse expressions due to their dynamic capabilities. However, in practice code tends to execute on the same types of objects each time. This means you can improve performance by remembering the results of method searches the first time a section of code executes. For example, with x + y, if x and y are integers the first time that expression executes, we can remember a code sequence or exactly what runtime function performs addition given two integers. Then each time that expression executes, there is no search involved. The code just checks that x and y are integers again, and dispatches to the right code with no searching. The result can literally be reduced to inlined code generation with a couple of type checks and an add instruction, depending on the semantics of an operation and method caching mechanisms used. [4]

[edit] C++

#include <iostream>
#include <string>
 
using namespace std;
 
class Animal
{
	public:
	Animal(const string& name) : name(name) { }
	virtual const string talk() = 0;
	const string name;
};
 
class Cat : public Animal
{
	public:
	Cat(const string& name) : Animal(name) { }
	virtual const string talk() { return "Meow!"; }
};
 
class Dog : public Animal
{
	public:
	Dog(const string& name) : Animal(name) { }
	virtual const string talk() { return "Arf! Arf"; }
};
 
// prints the following:
//
// Missy: Meow!
// Mr. Bojangles: Meow!
// Lassie: Arf! Arf!
//
int main()
{
	Animal* animals[] =
	{
		new Cat("Missy"),
		new Cat("Mr. Bojangles"),
		new Dog("Lassie")
	};
 
	for(int i = 0; i < 3; i++)
		cout << animals[i]->name << ": " << animals[i]->talk() << endl;
 
	return 0;
}

Note that the talk() method is explicitly declared as virtual. This is because polymorphic method calls have relatively high overhead in C++ [5]. This overhead is lessened by treating all method calls as non-polymorphic, unless explicitly marked as virtual by the developer.

[edit] Delphi

unit polymorphism;
 
interface
 
type
  IAnimal = interface
    property Name : string read; 
    function Talk : string;
  end;
 
  TAnimalBase = class( TObject )
  private
    fName : string
  public
    property Name : string read fName;
    constructor Create( aName : string ); virtual; 
  end;
 
  TCat = class( TAnimalBase, IAnimal )
  public
    function Talk : string;
  end;
 
  TDog = class( TAnimalBase, IAnimal )
  public
    function Talk : string;
  end;
 
implementation
 
constructor TAnimalBase.Create( aName : string );
begin
  fName := aName;
end; 
 
function TCat.Talk : string;
begin
  result := 'Meowww!';
end;
 
function TDog.Talk : string;
begin
  result := 'Arf! Arf!';
end;
 
end.

[edit] Perl

Polymorphism in Perl is inherently straightforward to write because of the languages use of sigils and references. This is the Animal example in standard OO Perl...

{
    package Animal;
    sub new {
        my ( $class, $name ) = @_;
        bless { name => $name }, $class;
    }
}
 
{
    package Cat;
    use base qw(Animal);
    sub talk  { 'Meow' }
}
 
{
    package Dog;
    use base qw(Animal);
    sub talk  { 'Woof! Woof!' }
}
 
my $a = Cat->new('Missy');
my $b = Cat->new('Mr. Bojangles');
my $c = Dog->new('Lassie');
 
for my $animal ( $a, $b, $c ) {
    say $animal->{name} . ': ' . $animal->talk;
}
 
# prints the following:
#
# Missy: Meow
# Mr. Bojangles: Meow
# Lassie: Woof! Woof!

This means that Perl can also apply Polymorphism to the method call. Example below is written using the Moose module to show modern OO practises in Perl (and is not needed for method Polymorphism).....

{
    package Animal;
    use Moose;
    has 'name' => ( isa => 'Str', is => 'ro' );
}
 
{
    package Cat;
    use Moose;
    extends 'Animal';
    sub talk  { 'Meow' }
    sub likes { 'Milk' }
}
 
{
    package Dog;
    use Moose;
    extends 'Animal';
    sub talk  { 'Woof! Woof!' }
    sub likes { 'Bone' }
}
 
my @animals = (
    Cat->new( name => 'Missy' ),
    Cat->new( name => 'Mr. Bojangles' ),
    Dog->new( name => 'Lassie' ),
);
 
for my $animal ( @animals ) {
    for my $trait qw/talk likes/ {
        say $animal->name . ': ' . $trait . ' => ' . $animal->$trait;
    }
}
 
# prints the following:
#
# Missy: talk => Meow
# Missy: likes => Milk
# Mr. Bojangles: talk => Meow
# Mr. Bojangles: likes => Milk
# Lassie: talk => Woof! Woof!
# Lassie: likes => Bone

[edit] Visual Basic .NET

One way of doing polymorphism is through the definition and implementation of a common interface. Consider the example below, where two subclasses (Cat and Dog) implement the IAnimal interface. Two Cat objects and one Dog are instantiated and given names, and then they are gathered in a list and their talk method is called.

Namespace std
 
    Public Interface IAnimal
        ReadOnly Property Name() As String
        Function Talk() As String
    End Interface
 
    Public Class Cat
        Implements IAnimal
 
        Private mName As String
 
        Sub New(ByVal name As String)
            mName = name
        End Sub
 
        Public ReadOnly Property Name() As String Implements IAnimal.Name
            Get
                Return mName
            End Get
        End Property
 
        Public Function Talk() As String Implements IAnimal.Talk
            Return "Meow!"
        End Function
    End Class
 
    Public Class Dog
        Implements IAnimal
 
        Private mName As String
 
        Sub New(ByVal name As String)
            mName = name
        End Sub
 
        Public ReadOnly Property Name() As String Implements IAnimal.Name
            Get
                Return mName
            End Get
        End Property
 
        Public Function Talk() As String Implements IAnimal.Talk
            Return "Arf! Arf!"
        End Function
    End Class
 
    Public Module TestAnimals
 
        ' Prints the following:
        '
        ' Missy: Meow!
        ' Mr. Bojangles: Meow!
        ' Lassie: Arf! Arf!
        Public Sub Main()
            Dim animals(2) As IAnimal
            animals(0) = New Cat("Missy")
            animals(1) = New Cat("Mr. Bojangles")
            animals(2) = New Dog("Lassie")
 
            For Each a As IAnimal In animals
                Console.Out.WriteLine("{0}: {1}", a.Name, a.Talk)
            Next a
 
        End Sub
    End Module
 
End Namespace

[edit] Xbase++

#include "class.ch"
 
//
//  This program prints:
//
//  Missy  Meow!
//  Mr. Bojangles  Meow!
//  Lassie  Bark!
//  Press any key to continue...
//
 
/////////////////////////////
//
PROCEDURE Main()
//
/////////////////////////////
 
  LOCAL aAnimals := Array(3)
  LOCAL i
 
  aAnimals[1] :=  Cat():New("Missy")
  aAnimals[2] :=  Cat():New("Mr. Bojangles")
  aAnimals[3] :=  Dog():New("Lassie")
 
  FOR i:=1 TO LEN(aAnimals)
     ? aAnimals[i]:Name + "  " + aAnimals[i]:Talk()
  NEXT i
 
  WAIT
 
RETURN
 
/////////////////////////////
//
CLASS Animal
//
/////////////////////////////
 
   EXPORTED:
      VAR Name   READONLY
 
      METHOD Init
      DEFERRED CLASS METHOD Talk
ENDCLASS
 
METHOD Animal:Init( cName )
   ::Name := cName
RETURN Self
 
/////////////////////////////
//
CLASS Dog FROM Animal
//
/////////////////////////////
   EXPORTED:
   METHOD Talk
ENDCLASS
 
METHOD Dog:Talk()
RETURN "Bark!"
 
/////////////////////////////
//
CLASS Cat FROM Animal
//
/////////////////////////////
   EXPORTED:
   METHOD Talk
ENDCLASS
 
METHOD Cat:Talk()
RETURN "Meow!"

[edit] Ruby

class Thing
  def who_am_I
    puts "I'm a thing!"
  end
end
 
class AnotherThing
  def who_am_I
    puts "I'm another thing!"
  end
end
 
#adds 'who_am_I' method to class String:
class String
  def who_am_I
    puts "I'm a plain Ruby String!"
  end
end
 
things = [Thing.new, AnotherThing.new, 'boring string']
things.each{|current_thing| current_thing.who_am_I}
 
#prints:
#
#I'm a thing!
#I'm another thing!
#I'm a plain Ruby String!

[edit] Parametric Polymorphism

In object-oriented programming languages, the term polymorphism has different, but related meanings; one of these, parametric polymorphism, is known as generic programming in the Object Oriented Programming Community and is supported by many languages including C++, C# and Java.

Generics allow you compile time type safety and other benefits and/or disadvantages depending on the language's implementation.

C++ implements parametric polymorphism through templates. The use of templates requires the compiler to generate a separate instance of the templated class or function for every permutation of type parameters used with it, which can lead to code bloat and difficulty debugging. A benefit C++ templates have over Java and C# is that they allow for template metaprogramming, which is a way of pre-evaluating some of the code at compile-time rather than run-time.

Java parametric polymorphism is called generics and implemented through type erasure.

C# parametric polymorphism is called generics and implemented by reification, making C# the only language of the three which supports parametric polymorphism as a first class member of the language. This design choice is leveraged to provide additional functionality, such as allowing reflection with preservation of generic types, as well as alleviating some of the limitations of erasure (such as being unable to create generic arrays). This also means that there is no performance hit from runtime casts and normally expensive boxing conversions. When primitive and value types are used as generic arguments, they get specialized implementations, allowing for efficient generic collections and methods.

[edit] Mitigating circular dependency references using polymorphism in C#

Problem

Assembly Name: Logger
Dependencies: Data Access Layer
Methods: LogError(string error)

Assembly Name: Data Access Layer
Dependencies: SqlClient, System.Data
Methods: WriteData(string data)

With this design, writing error messages from catch statements and writing them to the Logger class from the Data Access Layer class would not work due to a circular dependency reference.

Example of broken class in Data Access Layer

public static class WriteData
{
     public static void WriteData(string data, Logger logger) // not technically possible due to circular reference
     {
          try
          {
               SqlHelper.ExecuteNonQuery("my_stored_procedure", "some", "different", "arguments", data);
          }
          catch(Exception ex)
          {
               logger.LogError(ex); // not technically possible due to circular reference
          }
     }
}

Solution

Assembly Name: CommonClasses
Dependencies: None
Interfaces: ILogger

Assembly Name: Data Access Layer
Dependencies: SqlClient, System.Data, CommonClasses
Methods: WriteData(string data, ILogger logger)

Example of working class in Data Access Layer

public static class WriteData
{
     public static void WriteData(string data, ILogger logger)  // will compile and run fine
     {
          try
          {
               SqlHelper.ExecuteNonQuery("my_stored_procedure", "some", "different", "arguments", data);
          }
          catch(Exception ex)
          {
               logger.LogError(ex); // will compile and run fine
          }
     }
}

It is technically a better practice to have a static initialization function the caller uses to pass in a concrete class which implements the ILogger interface to the common classes API which caches it in a static private field with an associated public or internal property for reuse throughout the application without having to reinstantiate the object. Just try to avoid using static data, and if it has to be used, make sure to synchronize access to it for thread safety. [6]

[edit] See also

[edit] References

  1. ^ Sierra, Kathy; Bert Bates (2005). Head First Java, 2nd Ed.. O'Reilly Media, Inc.. ISBN 0596009208. 
  2. ^ Stroustrup, Bjarne (2000). The C++ Programming Language Special Edition. O'Reilly Media, Inc.. ISBN 0-201-70073-5. 
  3. ^ http://www.expertwebinstalls.com/csharptutorial/polymorphism-in-c/
  4. ^ http://msdn.microsoft.com/en-us/magazine/cc163344.aspx
  5. ^ Driesen, Karel and Hölzle, Urs, "The Direct Cost of Virtual Function Calls in C++", OOPSLA 1996
  6. ^ http://www.expertwebinstalls.com/csharptutorial/mitigating-circular-dependency-references-using-polymorphism-in-c/


[edit] External links

Personal tools
Languages