** 17. Object-Oriented Programming ** OO = abstract data types + inheritance/subtyping + dynamic dispatching ---------------------------------------------------------------------------- 17.1. abstract data types [also called classes]: =) group functions with related data *) often called constructors, methods, and instance variables =) ADT allows values of the type to be created (called instances) =) from outside, access e.g. with dot notation ("x.v", "x.f(...)") =) from inside, access directly, and have "self"/"this" special identifier *) encapsulate internal details *) give client narrower, cleaner, clearer interface *) give implementor freedom to change internal details w/o fear that clients might be affected *) extend language with new user-defined types *) seamlessly? i.e., are user-defined types different in any way than predefined types? e.g. pattern-matching, infix operations, literal forms, ... *) better support for modeling application domain in program ADTs vs. modules: =) ADTs implicitly define a single new type, with associated operations =) modules may have 0, 1, or many type components *) modules more general: *) can encapsulate operations over existing types (0 new types) *) can define group of interacting types, with internal details exposed within group but hidden from clients (e.g. List & Link) *) modules somewhat less convenient for common case (Stack.T vs. Stack) Both modules and ADTs can be parameterized by other modules/types In Core ML terms, modules & ADTs are records (some of whose components can be types, not just values), + some sort of hiding mechanism [full design: exercise for the reader] ---------------------------------------------------------------------------- 17.2. inheritance: =) allow one ADT to be implemented as a delta from an existing ADT *) delta can add new methods & instance variables *) delta can *override* methods [more later] =) single inheritance => a tree of sub- and superclasses =) multiple inheritance => a partial order [more later] +) inheritance as a code reuse mechanism (after the fact) +) inheritance as a code organization mechanism (in advance) *) less repetition of code => easier to debug & change *) encourages programming lots of variations *) can model classification hierarchies of application domain (except exceptions may be hard to model) subtyping: =) organize types into a tree or partial order of sub- and supertypes (t1 <= t2) =) a value of a subtype supports all the properties of values of any supertype, plus perhaps more =) allow a value of one type to be used where a value of a supertype is expected *) *subtype polymorphism*, in contrast to *parametric polymorphism* of ML & provided by Forall types in Core ML. *) an expression has a static type (the type given/inferred in the source text) as well as a dynamic type (the subtype that the run-time value really is an instance of); the dynamic type can change each time an expression is evaluated, but the static type is fixed *) subclasses *usually* are subtypes, since inheritance means they *usually* include all the properties of superclass, but perhaps more [more later] *) can define subtyping rules over predefined types like records, unions, and functions, too. Called *structural subtyping* rules. [more later] inheritance != subtyping: *) inheritance talks about reuse of implementation properties like method definitions and instance variable declarations *) subtyping talks about interface inclusion *) normal subclassing implies subtyping, but can easily have subtyping without inheritance, and in some cases can have inheritance without subtyping "syntactic" subtyping (interface inclusion) vs. behavioral subtyping (complete behavior inclusion) ---------------------------------------------------------------------------- 17.3. To allow more opportunities for code reuse/organization via inheritance, allow subclasses to *change* inherited properties, not just extend. E.g. allow a subclass to *override* an inherited method with an alternative one. *) like shadowing, but using inheritance chain instead of lexical scoping chain *) overridden method must have same # of arguments, but what about their types? what about the return type? [more later] *) overriding instance variables? [more later] When access a member f of an instance x of static type ST (x.f), if x's dynamic type is DT, want to access the implementation of f appropriate for DT, not ST. This is *dynamic dispatching* aka *dynamic method lookup* aka *virtual function lookup*. Dynamic dispatching is necessary only in the presence of overriding & subtyping. Super send: overriding method can be programmed as a delta from overridden method, by invoking overridden method within its body. Common Lisp has even more general forms of "method combination" than just super sends. Factoring: exploiting inheritance & dynamic dispatching to organize code into class hierarchies, with shared/similar code *factored* into common superclasses, and subclasses providing subclass-specific details via method overriding of hooks included in shared superclass. Abstract methods & classes: shared superclass may not have a good implementation for a hook, but only concrete subclasses. So can make superclass's method implementation "abstract" (unimplemented), requiring concrete classes to override the abstract method with a real, concrete one. A class with one or more abstract methods (locally defined or inherited) is an abstract class. Abstract classes cannot be instantiated directly, but are only good for subclassing from (and for putting shared, inheritable code in). A class with only abstract methods (a pure abstract class), which is essentially a pure interface or a pure type. Java's interfaces can be viewed as pure abstract classes. Since there's no code to inherit, one would inherit from a pure abstract class only to allow code to be written in terms of the pure abstract class and then be used for any subclass. Heavy use of abstract classes and factoring is a hallmark of good OO programming.