Abstract

Multiple inheritance is still controversial in the object-oriented community.  While notable languages such as C++ and Eiffel embrace the feature, it has been rejected by other significant languages, including Modula-3, Objective C and Java™.

We survey the advantages and disadvantages of multiple inheritance, as well as techniques that are often cited as alternatives.  Also, we present a language feature called automated delegation, which offers many of the advantages of multiple inheritance, as well as several additional benefits, while avoiding the principal drawbacks associated with multiple inheritance. Finally, we present Jamie, a preprocessor-based implementation of automated delegation for the Java™ programming language.

Acknowledgments

I would like to thank Reimer Behrends for his thorough critiques of my ideas, and for many discussions that had a positive impact on this work. Many others provided me with useful feedback on this work, for which I am grateful, including John Regehr, Tim Hollebeek, Anand Natrajan, Steve MacDonald, Paul Reynolds and the people at Reliable Software Technologies.

Special thanks go to Paul Reynolds, for all of the advice, support, opportunity and friendship he has given me.

Finally, I would like to thank my wife and family for enduring my time in school with me.

Table of contents

Abstract                                                                                                                     i

Acknowledgments                                                                                              ii

Table of contents                                                                                              iii

Chapter 1:   Introduction                                                                                 1

1.1    Thesis                                                                                                                                                            1

1.2    Motivation                                                                                                                                               1

1.3    Related work                                                                                                                                         3

1.4    Contributions                                                                                                                                         4

1.5    Terminology                                                                                                                                            5

1.5.1   Subclassing                                                                                                                                            5

1.5.2   Subtyping                                                                                                                                               6

1.5.3   Inheritance                                                                                                                                             7

1.5.4   Delegation                                                                                                                                             7

1.6    Organization                                                                                                                                          8

Chapter 2:   Advantages of Multiple Inheritance                            10

2.1    Multiple Specialization                                                                                                                 10

2.2    Mixin inheritance                                                                                                                               11

2.3    Multiple subtyping                                                                                                                            12

2.4    Pairing interfaces and implementations                                                                          13

2.5    Debatable examples                                                                                                                        14

2.5.1   Implementation Inheritance                                                                                                             14

2.5.2   Facility inheritance                                                                                                                           15

2.6    Summary                                                                                                                                                  15

Chapter 3:   Problems with Multiple Inheritance                           16

3.1    Name conflicts                                                                                                                                    16

3.2    Repeated inheritance                                                                                                                      18

3.3    Misuse                                                                                                                                                         19

3.4    Obscurity                                                                                                                                                 19

3.5    Summary                                                                                                                                                  19

 

Chapter 4:   Partial Solutions                                                                      21

4.1    Interfaces                                                                                                                                               21

4.2    Copy and modify                                                                                                                                  23

4.3    Base class modification                                                                                                               24

4.4    By-reference solutions                                                                                                                   25

4.5    Delegation                                                                                                                                              26

4.6    Summary                                                                                                                                                  28

Chapter 5:   Automated Delegation with Jamie                                 29

5.1    Basic forwarding with Jamie                                                                                                    30

5.2    Exclusion                                                                                                                                                 32

5.3    Dynamic features                                                                                                                              33

5.4    Delegating to static methods                                                                                                  36

5.5    Summary                                                                                                                                                  37

Chapter 6:   Design of Automated Delegation                                     38

6.1    Syntax                                                                                                                                                       38

6.2    Granularity                                                                                                                                          39

6.3    The evolution of the forwarder keyword                                                                         40

6.3.1   The “caller” keyword                                                                                                                        40

6.3.2   The “owner” keyword                                                                                                                       41

6.3.3   Early versions of “forwarder”                                                                                                         41

6.4    Modifiers                                                                                                                                                  42

6.4.1   Visibility modifiers                                                                                                                             42

6.4.2   Other modifiers                                                                                                                                   43

6.5    Universal base classes                                                                                                                   43

6.6    Summary                                                                                                                                                  44

Chapter 7:   Implementation of Jamie                                                      45

7.1    Overview                                                                                                                                                   45

7.1.1   The  forwards to  clause                                                                                                            45

7.1.2   The  forwarder implements  clause                                                                                    47

7.1.3   The forwarder keyword                                                                                                               50

7.2    Static methods                                                                                                                                    51

7.3    Why a preprocessor?                                                                                                                        53

7.4    Additional features                                                                                                                        53

7.5    Summary                                                                                                                                                  54

Chapter 8:   Analysis of Automated Delegation                                55

8.1    Automated Delegation vs. Multiple Inheritance                                                         55

8.2    Drawbacks of automated delegation                                                                               58

8.3    Automated Delegation vs. Single Inheritance                                                               60

8.4    Delegation in languages with multiple inheritance                                                61

8.5    Summary                                                                                                                                                  62

Chapter 9:   Related Work                                                                              63

9.1    Classless languages                                                                                                                        63

9.2    Hybrid Models                                                                                                                                       64

9.2.1   Object Specialization                                                                                                                        64

9.2.2   C++                                                                                                                                                      65

9.2.3   Smalltalk-style forwarding methods                                                                                               66

9.2.4   Transframe                                                                                                                                           68

9.3    Language support for mixins                                                                                                     70

9.3.1   Parametric polymorphism                                                                                                                70

9.3.2   Mixin construct                                                                                                                                   71

9.4    Dynamic subclassing                                                                                                                       72

9.4.1   Dynamic inheritance in Self                                                                                                             72

9.4.2   The become: message in Smalltalk-80                                                                                           72

9.4.3   Predicate classes in Cecil                                                                                                                 73

9.4.4   Declarative specialization in Java                                                                                                 74

9.5    Summary                                                                                                                                                  74

Chapter 10: Conclusions and Future Work                                           75

10.1 Conclusions                                                                                                                                            75

10.2 Future Work                                                                                                                                          76

References                                                                                                                              77

Appendix A                                                                                                                81


Chapter 1:         Introduction

This thesis presents automated delegation, which is a language feature meant as an alternative for multiple inheritance in class-based object oriented languages. As with multiple inheritance, automated delegation is a subclassing mechanism, where an object may acquire implementations from more than one superclass.  Unlike multiple inheritance, automated delegation is a dynamic feature; the derived implementation need not be determined statically.

 In this thesis, the benefits and drawbacks of multiple inheritance are enumerated.  It is then shown how automated delegation achieves a superset of the benefits multiple inheritance provides while avoiding the principal drawbacks.

This thesis also presents Jamie, an implementation of automated delegation that brings the benefits of multiple subclassing to the Java™ programming language.

1.1           Thesis

In languages with inheritance, a language feature that automates delegation, when coupled with a subtyping mechanism such as Java’s interfaces, can be a suitable replacement for multiple inheritance, providing not only most of the benefits that are traditionally found in multiple inheritance, but also many additional benefits. Also, automated delegation can avoid the principal drawbacks of multiple inheritance, namely repeated inheritance, misuse and unclear behavior.

1.2           Motivation

Consider writing in Java™ a class SortableVector that can be sorted by invoking a sort method. The preliminary design that suggests itself is to have a class SortableVector, which would inherit from class Vector as well as from class SortableCollection. It seems an appropriate design, since a sortable vector “IS-A” vector, and it “IS-A” sortable collection as well. Since Java does not provide multiple inheritance, the programmer must find an alternative implementation.

In order to reuse sorting code, one may keep it separate from the vector code. However, assuming the sorting code depends on some standard container functions implemented in the Vector class in order to perform the sorting, there would still need to be some communication between the sorting code and the Vector class. A common technique programmers use in such situations is delegation, which is familiar to many of those who program in Objective C [Cox84] and Java. Delegation is a technique where the programmer instantiates an object and forwards method calls from one class (called the delegator, or the forwarder) to the instantiated object (called the delegate). For example, here is a possible implementation of a SortableVector in Java:

class SortableVector extends Vector

{

      private SortableCollection sort_helper;

     

      SortableVector()

      {

// Pass "this" to the delegate’s constructor so it 

// can perform sorting operations on this instance.

            sort_helper = new SortableCollection(this);

      }

 

      void sort() throws SortingError

      {

            sort_helper.sort();

      }

}

 

In the above code, the sole responsibility of the method sort is to delegate the job of sorting to sort_helper (such a method is called a forwarding function).

Writing such code is a burden on the programmer, especially as interfaces get large.  He must remember to mirror the return type, argument list and throws clause when appropriate. He must make sure to either return a value or not as appropriate. Then, if the class he is using as a delegate changes, he must update his delegating object appropriately.

Suppose the programmer wants to limit the objects that can be inserted into the vector to be of type String.  Since Java offers no form of parametric polymorphism to provide this kind of type safety, he probably would change the methods by which the programmer may add data so that they would only accept data if the arguments are strings, leaving the other methods untouched. If using the Vector class in the standard Java library, the programmer would want to override three methods, and to reuse the implementation of twenty-one methods [CLK98]. Unfortunately, simple inheritance does not permit such reuse in Java, since all of the methods of class Vector are final, which means they may not be redefined in a subclass.  Thus, the programmer would either need to write his own vector implementation, or delegate to twenty-one methods.

While there may be valid reasons for not providing multiple inheritance in a language, situations like the above would be less problematic for the programmer if multiple inheritance were present. However, the work a programmer does may be automated, either by a language or by a language preprocessor.

1.3           Related work

Work related to automated delegation can be divided into language features that attempt to offer similar functionality and ad hoc strategies for simulating such functionality. 

Ad hoc techniques that are employed to simulate multiple inheritance include code duplication techniques, reference passing and delegation.  These solutions are presented in Chapter 4.  

Other languages offer alternatives to inheritance for either multiple subclassing or for dynamically changing inherited method implementations (dynamic subclassing).  Such work is presented in Chapter 9. 

1.4           Contributions

This work makes three contributions to the object oriented programming language community:

 

1.       A survey and critique of subclassing techniques:  This thesis both surveys and critiques previous multiple subclassing techniques including multiple inheritance, manual delegation and such language features as Predicate Classes in Cecil [Cha93].  For each of these techniques, the advantages and disadvantages are weighed, both from the programmer’s perspective, and, where appropriate, from the language designer’s perspective.  No similar survey of subclassing techniques has previously appeared in the literature.

 

2.       Automated delegation:  This thesis introduces and defines a language feature called automated delegation, which is a multiple subclassing feature similar to multiple inheritance. Automated delegation is also be compared to multiple inheritance, and is shown to have the following advantages:

·         Automated delegation is a more appropriate abstraction than is multiple inheritance for most of the scenarios in which multiple inheritance is generally considered appropriate.  For example, implementation inheritance and facility inheritance (discussed in Chapter 2) are naturally supported by automated delegation, yet are often criticized uses of multiple inheritance.

·         Automated delegation is more expressive than is multiple inheritance.  For example, it can be used to support dynamically changing implementations, which is not possible with multiple inheritance.

·         Automated delegation does not suffer from the principal drawbacks of multiple inheritance.  In particular, repeated inheritance and unclear behavior (obscure code) are avoided.

 

3.       Multiple subclassing for the Java programming language:  This thesis presents Jamie, which is a preprocessor based extension to Java that implements automated delegation.  For each aspect of Jamie’s design and implementation, the important design and implementation choices that were made are presented, as will be the effects those choices had on the final product. In cases where a different solution may be desirable in future systems, the ramifications of such alternatives are discussed.

1.5           Terminology

This section defines the terms that are most important to the rest of this work. Most of these terms are in common use within the object-oriented community, yet have no universally accepted definition.

1.5.1       Subclassing

Subclassing is the derivation of methods and possibly variables from another class. For example, inheritance, as is found in C++ [Str97], is a subclassing mechanism, since it allows the user to use methods from another class.

A subclassing relationship does not imply that type inheritance exists.  For example, Sather [Omo93] allows the programmer to use implementations as a kind of code inclusion; the subclassing mechanism does not perform type inheritance under any circumstances.

The implementation is unimportant, as long as the programmer may reuse code. For example, delegation is a form of subclassing, even though delegation usually uses a class instance to achieve reuse, as opposed to sharing the blueprint of a class.

Given a class X, a subclass of X is any class Y that performs subclassing in order to derive methods or data from class X. In such a case, X is considered to be a superclass of Y.

Multiple subclassing is directly subclassing from multiple (potentially unrelated) classes at once. A multiple subclassing mechanism should allow the programmer to subclass from an arbitrary number of classes at the same time.

A language feature that allows the programmer to subclass directly from an arbitrary number of classes is said to enable multiple subclassing, whereas a feature that limits that number is said to provide limited multiple subclassing.

While a class may be allowed to subclass from more than one class indirectly, a feature is not said to enable multiple subclassing unless the programmer may subclass from unrelated objects.

1.5.2       Subtyping

Subtyping is the ability for a data type (usually a class) to derive its type from another data type. An instance of the derived type may be substituted for an instance of the base type, although the reverse may not always be true.  If class Y is a subtype of class X, then class Y must share class X's signature. In other words, class Y's methods and data must be a superset of those provided by class X. In the Java programming language [AG96], there is a special type of class that is a signature completely devoid of implementation, called an interface.  A Java interface consists only of method signatures; data may not be part of an interface[1]. In addition to subtyping through inheritance, a Java, class may also subtype by implementing an interface, which is explicitly declaring that the class provides implementations for all of the methods in the interface.  A class is said to fulfill an interface if it provides implementations for all of the methods in an interface.  A class may fulfill an interface without implementing it.

Multiple subtyping is the ability to subtype from multiple classes at the same time. In Java, the programmer may subtype as many times as he wishes, as long as he does so via the interface mechanism.

1.5.3       Inheritance

There is no universally accepted definition for inheritance.