Polymorphism in object-oriented programming
From Wikipedia, the free encyclopedia
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
- ^ Sierra, Kathy; Bert Bates (2005). Head First Java, 2nd Ed.. O'Reilly Media, Inc.. ISBN 0596009208.
- ^ Stroustrup, Bjarne (2000). The C++ Programming Language Special Edition. O'Reilly Media, Inc.. ISBN 0-201-70073-5.
- ^ http://www.expertwebinstalls.com/csharptutorial/polymorphism-in-c/
- ^ http://msdn.microsoft.com/en-us/magazine/cc163344.aspx
- ^ Driesen, Karel and Hölzle, Urs, "The Direct Cost of Virtual Function Calls in C++", OOPSLA 1996
- ^ http://www.expertwebinstalls.com/csharptutorial/mitigating-circular-dependency-references-using-polymorphism-in-c/