CSC/ECE 517 Fall 2007/wiki2 8 42
Author: Matthew B. Simmons
Objective
The objective of this document is to briefly review overloading and overriding and to compare/contrast the different way overriding is handled in multimethod programming languages (like CLOS) and non-multimethod languages (like C#, Java or C++).
Overloading
Method overloading occurs when one or more methods within the scope of a class share the same name but differ in their parameter list. Parameters are differentiated by their type and not their names; parameter names can be changed at will (but of course should properly relate to the objects they represent). For example, the following class definition contains an overloaded method:
public class Pages { // add an integer to this list of pages public bool AddPage(int page) { } // parse the string argument to an int and // add it to this list of pages (via overloaded AddPage(int)) public bool AddPage(string page) { } }
Notice that there are two methods named AddPage(), but the type of their argument differs; one method accepts an integer and adds it to an accumulator and one method accepts a string, first parsing it to an int and then adding it to an accumulator (via the AddPage(int) method). Ultimately both methods accomplish the same task -- which is a fundamental design rule when choosing to overload methods. Methods should never be overloaded unless their functionality closely mirrors that of the method(s) that it overrides. It is counterintuitive to have one AddPage() method that adds pages to an accumulator and one AddPage() method that determines the temperature in Guatemala!
Overriding
Where overloading was creating methods with the same name and different arguments (and therefore a different signature), overriding occurs when a method replaces another method further up the inheritance hierarchy that has the same signature. For example, consider the following classes:
public class Button { // in C#, the keyword virtual indicates that a method can be overridden public virtual void OnClick(object sender, EventArgs e) { } } public class ToggleButton : Button { // in C#, overriding is an EXPLICIT action by using the keyword override public override void OnClick(object sender, EventArgs e) { } }
The ToggleButton class inherits directly from the Button class. ToggleButton has a method OnClick() that overrides the OnClick() method of Button that was inherited. Notice that C# above mandates explicit overriding (the use of the keyword override) -- other languages such as Java allow for implicit overriding, where methods with the same signature automatically override their inherited methods without the use of an override keyword.
One major advantage of overriding methods is that you get polymorphic behavior. For example, if a List object was created and populated as...
List<Button> buttons = new List<Button>(); buttons.add(new Button()); buttons.add(new ToggleButton()); buttons.add(new Button());
...then polymorphism allows the following actions with the displayed results:
foreach (Button button in buttons) button.OnClick(this, new EventArgs()); // iteration 1 -- calls Button's OnClick() // iteration 2 -- calls ToggleButton's OnClick() // iteration 3 -- calls Button's OnClick()
This dynamic method invocation (the decision about which method to call being made at runtime) is the polymorphic behavior that was produced from overriding the OnClick() method.
CLOS (see-loss or kloss) and Multi-Method Language Overriding
Common List Object System (CLOS) is a dynamic object oriented language that is an extension of Common Lisp. It is a multi-method language. A multi-method language is distinguishable from non multi-method languages by the decision mechanism by which overridden methods are chosen at runtime.
In more common object oriented languages (like Java or C#), the runtime decision about which overloaded is made is based on the type of the object being sent the message and NOT the type of the arguments to that message. At compile time, the type of the arguments is used to determine an appropriate method in the set of overloaded methods that match a given signature, but this action is strictly a strategy of the compiler to check for valid code. At runtime, the type of the arguments play no role.
For example, consider the following example in C#:
public class Person { public string name = string.Empty; } public class User : Person { } public class Administrator : Person { } public class Maintainer : Person { } public class FormatPrint { public void Print(User person) { MessageBox.Show("User"); } public void Print(Administrator person) { MessageBox.Show("Administrator"); } public void Print(Maintainer person) { MessageBox.Show("Maintainer"); } }
The following code will not run using C# because it is unable to decide which Print() method to call!
FormatPrint formatPrint = new FormatPrint(); Person person = new Administrator(); print.Print(person);
C# is unable to determine which method to call because it is unable to figure out what person really is because it is an argument -- the fact that it is an Administrator is ignored! However, a multi-method language such as CLOS considers the argument types at runtime and will determine which Print() method to call.
C# and Non Multi-Method Language Overriding
Unlike CLOS, C# (and other popular object oriented languages like Java) is not a multi-method language so the decision about which overridden method to call at runtime is based on the type of the object being sent the message. Consider the following modification to the above code:
public class Person { public string name = string.Empty; public virtual void Print() { }; } public class User : Person { public override void Print() { MessageBox.Show("User"); } } public class Administrator : Person { public override void Print() { MessageBox.Show("Administrator"); } } public class Maintainer : Person { public override void Print() { MessageBox.Show("Maintainer"); } }
Note that the following code decides which overridden method to call based on the type of argument before the message Print().
List<Person> people = new List<Person>(); people.Add(new Administrator()); people.Add(new Maintainer()); people.Add(new User()); // prints "Administrator" people[0].Print(); // prints "Maintainer" people[1].Print(); // prints "User" people[2].Print();
Summary
Overloading happens when methods in the scope of a class share the name but differ in the type of arguments passed. The methods do not share a common signature. Conversely, overriding a method happens when the method a method has the exact signature of one that the class inherited -- in which case the new method replaces the old one. Multi-method languages differ from non-multi-method languages in how they decide which of the method to invoke. Multi-method languages use the type of the arguments whereas non multi-method languages use the type of the object being sent the message.