Dynamic Binding: Class-Appropriate behavior

Steven Zeil

Last modified: Aug 9, 2019
Contents:

To be considered “object-oriented”, a language must support inheritance, subtyping, and dynamic binding. We have seen the first two. Now it’s time to look at the third.

If you want to claim that a program is written in an object-oriented style, the code must be designed to take advantage of these three features. Otherwise, it’s just a traditionally styled program written in an object-oriented programming language (OOPL).

1 Dynamic Binding

Dynamic binding is a key factor in allowing different classes to respond to the same message with different methods.

1.1 What is dynamic binding?

Binding is a term used in programming languages to denote the association of information with a symbol. It’s a very general term with many applications.


Binding Function Calls to Bodies

In OOP, we are particularly interested in the binding of a function body (method) to a function call.

Given the following:

a = foo(b);

When is the decision made as to what code will be executed for this call to foo?

1.1.1 Compile-Time Binding

  a = foo(b);

In traditionally compiled languages (FORTRAN, PASCAL, C, etc), the decision is made at compile-time.

1.1.2 Run-Time Binding

a = foo(b);

In traditionally interpreted languages (LISP, BASIC, etc), the decision is made at run-time.

1.1.3 Dynamic Binding = the Happy Medium?

OOPLs typically feature dynamic binding, an “intermediate” choice in which


Dynamic Binding and Programming Languages

1.2 Dynamic Binding in C++

1.2.1 Virtual functions

1.2.2 Calling via Pointers or References

2 Dynamic Binding in Java

Dynamic binding is more pervasive in Java, because

3 An Example of Dynamic Binding

3.1 C++: An Animal Inheritance Hierarchy

For this example, we will introduce a simple hierarchy.

3.1.1 The Base Class

class Animal {
public:
  virtual String eats() {return "???";}
  String name() {return "Animal";}
};          

We begin with the base class, Animal, which has two functions.

3.1.2 Plant Eaters

Now we introduce a subclass of Animal that overrides both those functions.

class Herbivore: public Animal {
public:
   virtual String eats() {return "plants";}
   String name() {return "Herbivore";}
};

3.1.3 Cud-Chewers

Now we introduce a subclass of that class.

class Ruminants: public Herbivore {
public:
   virtual String eats() {return "grass";}
   String name() {return "Ruminant";}
};          

3.1.4 Meat Eaters

And another subclass of the original base class.

class Carnivore: public Animal {
public:
   virtual String eats() {return "meat";}
   String name() {return "Carnivore";}
};          

3.1.5 The Application Code

Output Function

It will also be useful in this example to have a simple utility function to print a pair of strings.

void show (String s1, String s2) {
          cout << s1 << " " << s2 << endl;
}          

Finally, we introduce some application code that makes calls on the member functions of our inheritance hierarchy.


Let’s Make Some Calls

 Animal a, *paa, *pah, *par;
 Herbivore h, *phh;
 Ruminant r;
 paa = &a; phh = &h; pah = &h; par = &r;
 
 show(a.name(), a.eats());      // AHRC ?pgm
 show(paa->name(), paa->eats()); // AHRC ?pgm
 show(h.name(), h.eats);        // AHRC ?pgm
 show(phh->name(), phh->eats()); // AHRC ?pgm
 show(pah->name(), pah->eats()); // AHRC ?pgm
 show(par->name(), par->eats()); //AHRC ?pgm

Note the variety of variables we are using.

3.1.6 What’s the output?

 Animal a, *paa, *pah, *par;
 Herbivore h, *phh;
 Ruminant r;
 paa = &a; phh = &h; pah = &h; par = &r;
 
 show(a.name(), a.eats());      
 show(paa->name(), paa->eats()); 
 show(h.name(), h.eats);        
 show(phh->name(), phh->eats()); 
 show(pah->name(), pah->eats()); 
 show(par->name(), par->eats()); 

Question: What will be the output of the various show calls?

See if you can work this out for yourself before moving on.

first prev1 of 8next last

3.2 The Animal Example in Java

Same example, this time in Java. Start with the inheritance hierarchy.

3.2.1 The Classes

public class Animal {
  public String eats() {return "???";}
  public name() {return "Animal";}
}

public class Herbivore extends Animal {
   public String eats() {return "plants";}
   public String name() {return "Herbivore";}
}

public class Ruminants extends Herbivore {
   public String eats() {return "grass";}
   public String name() {return "Ruminant";}
}

public class Carnivore extends Animal {
   public String eats() {return "meat";}
   public String name() {return "Carnivore";}
}

3.2.2 The Application

public class AnimalTest {

  private static void show (String s1, String s2) {
     System.out.println (s1 + " " + s2);
  }

  public static void main (String[]) {
     Animal a = new Animal();
     Herbivore h = new Herbivore();
     Ruminant r = new Ruminant();

     Animal paa = a;
     Animal pah = h;
     Animal par = r;
     
     show(a.name(), a.eats());
     show(paa.name(), paa.eats());
     show(h.name(), h.eats);
     show(pah.name(), pah.eats());
     show(par.name(), par.eats());
  }
}

The application is structured a bit differently. Java does not allow standalone functions, so the show function needs to be inside a class. We assume the rest of the application code is in that same class.

Note the absence of the * and & operators.

3.2.3 The Output

public static void main (String[]) {
   Animal a = new Animal();
   Herbivore h = new Herbivore();
   Ruminant r = new Ruminant();

   Animal paa = a;
   Animal pah = h;
   Animal par = r;

   show(a.name(), a.eats());
   show(paa.name(), paa.eats());
   show(h.name(), h.eats);
   show(pah.name(), pah.eats());
   show(par.name(), par.eats());
}

Output

animal ???

Although a looks like an ordinary variable to C++ programmers, in Java a is a pointer/reference to an actual object on the heap.

Couple that with the idea that all ordinary member functions in Java are virtual, and unlike C++, both calls in this line will be resolved by dynamic binding.

So, at runtime, the program follows a out to the heap, discovers that it actually points to an Animal, and invokes the Animal.name() function body to print “Animal”. Then it does the same for eats() and invokes the Animal.eats() function body to print “???”.

first prev1 of 5next last

The overall output here is slightly different from that in the earlier C++ example because, in Java, the name() function is “virtual” and so is handled by dynamic binding.

4 Why is Dynamic Binding Important?

Dynamic binding lets us write application code for the superclass that can be applied to the subclasses, taking advantage of the subclasses’ different methods.

We will explore this idea in the next lesson.