Extending Class Behavior Inheritance Polymorphism and Interfaces Adding
Extending Class Behavior Inheritance, Polymorphism, and Interfaces
Adding Functionality An Introduction to Inheritance
Introduction • How do we enhance the functionality of an existing class? • Examples: – Adding color facilities to an edit window – Adding a sort capability to a hash table class – Adding a middle name to a Name object
Modify the Existing Class? • May not be desirable – Class is already rigorously tested and robust – Additional complexity of enlarged class – Source may not be available – Modification may not be advisable (e. g. , Java predefined classes)
Using Composition • Create a new class that contains an instance variable referring to the existing class Existing. Class() ··· New. Class() ··· ec ··· • There are two independent objects here!
Composition in Java class New. Class { New. Class(. . . ) {. . . ec = new existing. Class(. . . ); . . . }. . . Existing. Class ec; } New. Class Existing. Class() ··· New. Class() ··· ec ··· + It is the responsibility of the containing class to create the contained object.
Drawbacks of Composition New. Class nc = new New. Class(. . . ); Existing. Class() m 1() m 2() ··· mn() private. . . ; ··· nc. m 1(…) New. Class() ··· ec ··· + How do we invoke mi (of Existing. Class) via the New. Class object? ? ? + What about the private instance variables of Existing. Class?
Inheritance • Extends the original class to form a new class using inheritance. Existing. Class() ··· New. Class() ··· • There is only one object here!
Inheritance • The new object consists of – All methods of the existing class – All instance variables of the existing class – Any new methods introduced by the new class – Any new instance variables introduced by the new class Existing. Class() ··· New. Class() ···
Subclass and Superclass • The new class is called the subclass. • The existing class is called the superclass. Superclass (Existing. Class) Existing. Class() ··· Subclass (New. Class) New. Class() ···
Inheritance in Java class New. Class extends Existing. Class { } • Without another line of code, New. Class inherits the full behavior of Existing. Class! Existing. Class() ··· New. Class
Availability of Existing Behavior New. Class nc = new New. Class(…); nc. m 1(…); • Can invoke any method of Existing. Class using the New. Class object as the receiver. nc. m 1(…) Existing. Class() m 1() m 2() ··· mn() private …; ··· New. Class
A Better Buffered. Reader A First Example of Inheritance
Class Buffered. Reader(Reader) String read. Line() ···
Drawbacks of Buffered. Reader • Somewhat awkward to construct Buffered. Reader br = new Buffered. Reader( new Input. Stream. Reader ( new File. Input. Stream( new File(“assgmt 1. txt”); • No facilities for numeric input. int i = Integer. parse. Int(br. read. Line());
Behavior of a Better Buffered. Reader • Construct using just a file name • Provide numeric input
Interface for a Better Buffered. Reader • Better. BR(String name) • int read. Int()
Better. BR- A Better Buffered. Reader(Reader) String read. Line() ··· Better. BR(String name) int read. Int() ···
Class Better. BR class Better. BR extends Buffered. Reader { Better. BR(String name) {…} int read. Int() {…}. . . } Buffered. Reader(Reader) String read. Line() ··· Better. BR(String name) int read. Int() ···
Initializing the Superclass Object Better. BR br = new Better. BR(“assgmt 1. txt”); • How is the Buffered. Reader object initialized? (who calls the constructor for the Buffered. Reader object? ) Buffered. Reader(Reader) String read. Line() ··· Better. BR(String name) int read. Int() ···
Initializing the Superclass Object (II) • The subclass is responsible for invoking the superclass’s constructor. • It does this using the super keyword Better. BR(String name) { super(…); } Buffered. Reader(Reader) String read. Line() ··· Better. BR(String name) int read. Int() ···
Class Better. BR(String file. Name) throws IOException { super(new Input. Stream. Reader( new File. Input. Stream(new File(file. Name)))); } Better. BR() throws IOException { super(new Input. Stream. Reader(System. in)); } • Overloaded constructor provides creation from System. in Better. BR(String name) int read. Int()
Class Better. BR int read. Int() throws IOException { String line; line = read. Line(); return Integer. parse. Int(line); } Better. BR(String name) int read. Int()
Using Better. BR int i; String s; Better. BR bbr = new Better. BR("junk"); i = bbr. readint(); // Read in some integers while (i >= 0) { System. out. println("i = " + i); i = bbr. readint(); } s = bbr. read. Line(); // Now read in some Strings while (s != null) { System. out. println("s = " + s); s = bbr. read. Line();
Extending a Name Class Adding State
A Name Class Name() get. Initials() get. Last. First() get. First. Last() set. Title() first. Name last. Name title
A Name Class class Name { public Name(String first, Stringlast){…} public String get. Initials() {…} public String get. Last. First() {…} public String get. First. Last() {…} public void set. Title(String new. Title) {…}. . . } Name() get. Initials() get. Last. First() get. First. Last() set. Title() first. Name last. Name title
A Name Class class Name {. . . private String } first. Name; last. Name; title; Name() get. Initials() get. Last. First() get. First. Last() set. Title() first. Name last. Name title
A Name Class public Name(String first, String last) { first. Name = first; last. Name = last; title = ""; } Name() get. Initials() get. Last. First() get. First. Last() set. Title() first. Name last. Name title
A Name Class public String get. Initials() { String s; s = first. Name. substring(0, 1); s = s. concat(". "); s = s. concat(last. Name. substring(0, 1)); s = s. concat(". "); return s; } Name() get. Initials() get. Last. First() get. First. Last() set. Title() first. Name last. Name title
A Name Class public String get. Last. First() { return last. Name. concat(", "). concat(first. Name); } Name() get. Initials() get. Last. First() get. First. Last() set. Title() first. Name last. Name title
A Name Class public String get. First. Last() { return title. concat(" "). } "). concat(last. Name); concat(first. Name). concat(" Name() get. Initials() get. Last. First() get. First. Last() set. Title() first. Name last. Name title
A Name Class public void set. Title(String new. Title) { title = new. Title; } Name() get. Initials() get. Last. First() get. First. Last() set. Title() first. Name last. Name title
How About Adding a Middle Name? • Don’t want to modify Name class • Instead we’ll create a new subclass, Extended. Name + What about using composition?
Behavior for an Extended Name • constructor • get middle initial • How about a ‘formal name’ – title + first name + middle initial + last name • We’ll have to maintain the middle name as part of the state
Adding State to the Subclass • Inheritance allows instance variables to be introduced as well. Existing. Class() ··· New. Class() ··· new. Var
Class Extended. Name() get. Initials() get. Last. First() get. First. Last() set. Title() first. Name last. Name title Extended. Name() get. Middle. Initial() get. Formal. Name() middle. Name
Extended. Name class Extended. Name extends Name { public Extended. Name(String first. Name, String middle. Name, String last. Name) {…} public Extended. Name(String first. Name, String last. Name) {…} public String get. Middle. Initial() {…} public String get. Formal. Name() {…} } private String middle. Name; Extended. Name() get. Middle. Initial() get. Formal. Name() middle. Name
Extended. Name public Extended. Name(String first. Name, String middle. Name, String last. Name) { super(first. Name, last. Name); this. middle. Name = middle. Name; } public Extended. Name(String first. Name, String last. Name) { super(first. Name, last. Name); this. middle. Name = ""; } Extended. Name() get. Middle. Initial() get. Formal. Name() middle. Name
Extended. Name public String get. Middle. Initial() { if (!middle. Name. equals("")) return middle. Name. substring(0, 1); else return "NMI"; } + Is “NMI” really a good choice? ? Extended. Name() get. Middle. Initial() get. Formal. Name() middle. Name
Extended. Name public String get. Formal. Name() { return title + " " + first. Name + " " + get. Middle. Initial() + " " + last. Name; } Extended. Name() get. Middle. Initial() get. Formal. Name() middle. Name + Uh-oh!, those variables are private to Name!!
Accessing the Superclass’s State • Use the keyword protected for the superclass’s instance variables. • Allows access by subclass.
Revising the Name Class class Name {. . . protected String } first. Name; last. Name; title; Name() get. Initials() get. Last. First() get. First. Last() set. Title() first. Name last. Name title
Private vs Protected? Religion war
The Consequences of Inheritance A Sane Approach to Using Inheritance
Inheritance as the ‘is-a’ Relationship • A subclass object is also a superclass object – It possesses all the methods and state of the superclass – Subclass object can thus be used anywhere a superclass object can • We say a subclass object is-a superclass object
What This Means in Java • Extended. Name ex. Name = new Extended. Name(“John”, “Quincy”, ”Adams”); Name name; name = ex. Name; 3 • Name name = new Extended. Name(“John”, “Quincy”, “Adams”); 3
What about the Other Way? • Extended. Name ex. Name; Name name = new Name(“John”, “Brown”); ex. Name = name; 7 + Name objects cannot be substituted for Extended. Name objects!! (Why not? )
Class Hierarchies • class Extended. Name 2 extends Name {…} class Extended. Name 3 extends Extended. Name{…} Name Extended. Name 3 Extended. Name 2
What This Means in Java (II) Name name = new Extended. Name(…); name = new Extended. Name 2(…); name = new Extended. Name 3(…); Extended. Name ename = new Extended. Name 3(…); ename = new Extended. Name 2(…); 3 3 7 Name Extended. Name 3 Extended. Name 2
What This Means in Java (III) Name name = new Extended. Name(…); Extended. Name e. Name = (Extended. Name)name; name = new Extended. Name 2(…); ename = (Extended. Name)name; 3 3 3 7 Name Extended. Name 3 Extended. Name 2
Red-Light/Green-Light Name Extended. Name 3 Extended. Name 2 OK Cast required, possible Exception Compiler error
Object - The Mother of All Classes • The root of the Java class hierarchy is the Object class • Any method contained in Object is thus inherited by all classes. – to. String – equals Object
Object and Collections • Collection classes accept references to Object void add. Element(Object o); // in class Vector • Similarly, Enumeration’s next. Element returns references to Object next. Element(); // in Enumeration
Object and Collections (II) • This allows collections to contain any element type (except for? ) • However when retrieving, a cast is required. String s = (String) e. next. Element
Composition as the ‘has-a’ Relationship • Composition reflects a has-a relationship. • No subclass object, only a reference • No inheritance New. Class Existing. Class New. Class() Existing. Class() ··· ··· ec
Inheritance or Composition? • If all methods of existing class should be methods of new class, use inheritance • If only some methods, use composition
Inheritance or Composition? (II) • If can say ‘new object is-a existing object’, use inheritance • If can say ‘new object has-a existing object’, use composition.
Inheritance or Composition? (III) • Finally, keep in mind Java allows single inheritance only
That’s Some Inheritance! Utilizing Complex Functionality Through Inheritance
Another Example - A Window • Want to be able to use a graphic window. • Need to handle – Resizing – Minimizing/Restoring – Title bar manipulation – … and a slew of other issues
Another Example - A Window • All this in addition to the actual task we wish to perform
Using a Window Superclass Window() on. Resize() on. Iconize() set. Title. Bar() get. Dimensions() ··· My. Window() do. My. Stuff() draw() ···
Leveraging Existing Functionality • We’ll actually put this concept into action when we work with the Abstract Window Toolkit
Different Strokes for Different Folks Polymorphism
Method Overriding • Discrepancy in get. Initials method of Extended. Name • Need to redefine that method in the subclass • The new method overrides the original method of class Name
An Overridden Method Name() get. Initials() get. Last. First() get. First. Last() set. Title() first. Name last. Name title Extended. Name() get. Middle. Initial() get. Formal. Name() get. Initials() middle. Initial
Invoking an Overriden Method • Consider Name name = new Name(“Gerald Weiss”); System. out. print(name. get. Initials()); Name() get. Initials() get. Last. First() get. First. Last() set. Title() first. Name last. Name title
Invoking an Overriden Method (II) • and Extended. Name ex. Name = new Extended. Name(“David”, “Moss”, “Arnow”); System. out. print(ex. Name. get. Initials()); Name() get. Initials() get. Last. First() get. First. Last() set. Title() first. Name last. Name title Extended. Name() get. Middle. Initial() get. Formal. Name() get. Initials() middle. Initial
Invoking an Overriden Method (III) • Now, what about Name name = new Extended. Name(“David”, “Moss”, “Arnow”); System. out. print(name. get. Initials()); Name() get. Initials() get. Last. First() get. First. Last() set. Title() first. Name last. Name title ? Name() get. Initials() get. Last. First( ) get. First. Last( ) set. Title() first. Name last. Name title Extended. Name() get. Middle. Initial() get. Formal. Name() get. Initials() middle. Initial
Static vs Dynamic Binding • Name is the static (compile-time) type of the reference variable • However, the dynamic (run-time) type of the object it references is Extended. Name!
The Law of Polymorphism When invoking overriden methods, the type of the actual object is what counts, not the type of the reference variable. Method matching thus begins in the object’s class, not the class of the reference variable.
Polymorphism • Therefore Name name = new Extended. Name(“David”, “M”, “Arnow”); System. out. print(name. get. Initials()); Name() get. Initials() get. Last. First() get. First. Last() set. Title() first. Name last. Name title Extended. Name() get. Middle. Initial() get. Formal. Name() get. Initials() middle. Initial !
Overriding vs. Overloading • Same name for two distinct methods • Static binding (method resolution) Overriding • Allows redefinition of method in subclass • Dynamic binding System. out. print(i) name. get. Initials()
Factoring Out Common Behavior Another Application of Inheritance
Another Use for Inheritance • We’ve just seen inheritance used to extend functionality • Can be used for other purposes as well
An Inventory System • Lenses • Film • Cameras
Properties of Lenses • Possess focal length • Zoom?
Properties of Film • Recommended storage temperature • Film speed • Number of exposures
Properties of Cameras • Lens included? • Maximum shutter speed • Body color
Properties of All Inventory Items • • Description Inventory ID Quantity on hand Price
The Three Classes Lens() get. Description() get. Qty. On. Hand() get. Price() Lens-specific() description inventory. ID qty. On. Hand price is. Zoom focal. Length Film() get. Description() get. Qty. On. Hand() get. Price() Film-specific() description inventory. ID qty. On. Hand price temp speed exposures • Notice the redundancy!!! Camera() get. Description() get. Qty. On. Hand() get. Price() Camera-specific() description inventory. ID qty. On. Hand price has. Lens max. Shutter body. Color
Factor Out the Common Behavior into a Superclass! • Each class is actually modelling two entities – A generic inventory item – A specific item— lens, film, camera • Remember, OOP is also responsibilitydriven programming!! – Split the responsibilities
An Inventory. Item Superclass Inventory. Item { Inventory. Item(…) {…} String get. Description() {…} int inventory. ID() {…} int get. Qty. On. Hand() {…} int get. Price() {…} } String description; int inventory. Number; int qty. On. Hand; int price; Inventory. Item() get. Description() get. Qty. On. Hand() get. Price() description inventory. ID qty. On. Hand price
The Three Subclasses Inventory. Item Invemtory. Item() get. Description() get. Qty. On. Hand() get. Price() description inventory. ID qty. On. Hand price Lens() Lens-specific() is. Zoom focal. Length Film() Film-specific() temp speed exposures Inventory. Item Invemtory. Item() get. Description() get. Qty. On. Hand() get. Price() description inventory. ID qty. On. Hand price Camera() Camera-specific() has. Lens max. Shutter body. Color
The Lens Class Code class Lens extends Inventory. Item { Lens(…) {…}. . . Lens-specific methods. . . boolean is. Zoom; double focal. Length; } Inventory. Item Invemtory. Item() get. Description() get. Qty. On. Hand() get. Price() description inventory. ID qty. On. Hand price Lens() Lens-specific() is. Zoom focal. Length
Working with the Class Hierarchy Inventory. Item [] invarr = new Inventory. Item[3]; invarr[0] = new Lens(…); invarr[1] = new Film(…); invarr[2] = new Camera(…); for (int I = 0; I < invarr. length; I++) System. out. println( invarr[I]. get. Description() + “: “ + invarr[I]. get. Qty. On. Hand() + “ available”); Lens Inventory. Item [] Film Camera
Getting Below the Superclass • Our print code only displayed the common elements of the superclass • What about the individual object’s data? – focal length and zoom for lens – speed and temperature for film • Inventory. Item does not know about these properties!!
Use Polymorphism! • Add a print method to each subclass Lens Film print() ··· ··· Camera print() ··· • Define a print method in the superclass Inventory. Item print() ···
Sample Code class Inventory. Item { ··· void print() {…} ··· } Inventory. Item print() ··· class Lens extends Inventory. Item { ··· void print() {prints Lens-specific data} ··· } • Similarly for Film and Camera Lens print() ···
Polymorphism is Now Available • The subclass methods then override the superclass’s and can be invoked polymorphically through the superclass! Inventory. Item inv = new Lens(…); inv. print(); inv Lens print() ···
Leaving it to the Kids Abstract Methods and Classes
Two Superclass Issues (I) • Inventory. Item does not model a real object but rather a generic concept – Should not be allowed to create an instance of this class
Two Superclass Issues (II) • The print method of Inventory. Item exists only to be overridden – We therefore do not wish to implement it in the superclass – It must be specified in the superclass so that it may be invoked – Finally, the method must be overridden by the subclass
Abstract Methods • The keword abstract in a method header – Specifies the existence of a method but not its implementation • In Inventory. Item abstract void print(); No body!
Abstract Classes • Any class that contains at least one abstract method • Must be indicated with keyword abstract in class header abstract class Inventory. Item {. . . abstract void print() {…}. . . }
Abstract Classes • Cannot create instances of an abstract class Inventory. Item inv = new Inventory. Item(…); 7 • Reference variables, though may be declared and assigned instances of a subclass Inventory. Item inv = new Lens(…); 3
Leaving it ALL to the Kids interface
Specifying Common Behavior (I) • Sometimes we just wish to specify the existence of behavior- no implementation • An abstract class containing only abstract methods
Specifying Common Behavior (II) • Abstract classes force behavior within a hierarchy abstract (super)class subclass • Sometimes we wish to require behavior of objects with no hierarchical relationship – Can’t use an abstract superclass
Examples (I) • Enumeration abstract class Enumeration { abstract Object next. Element(); abstract boolean has. More. Elements(); } Enumeration has. More. Elements() next. Element() linked list disk buffer score and seven …. . input stream tree
Examples (II) • Sorting – Requires the array elements be comparable Sortable is. Less. Than() if a[i]. is. Less. Than(a[i+1]) swap
What About Using Inheritance? • In both cases – No implementation would be inherited • Would be wasting our one superclass • Would rather inherit useful behavior implementation – The behavior can’t be fitted onto the hierarchy
A Different Relationship • The relationship is – conformance to a specified set of behavior – NOT superclass/subclass
Interfaces • Nonclass type consisting of only abstract methods • Uses keyword interface rather than class
The Enumeration interface Enumeration { Object next. Element(); boolean has. More. Elements(); }
The Sortable interface Sortable { boolean is. Less. Than(Sortable s); }
Implementing Interfaces • A class implements an interface • All methods of the interface must be implemented • More than one interface may be implemented class implements Sortable, Cloneable {. . . }
Implementing Enumeration class Vector. Enumeration implements Enumeration { Vector. Enumeration(…) {…} Object next. Element() {…} boolean has. More. Elements() {…} } • Then: class Vector { … Enumeration elements() { return new Vector. Enumeration(…); } }
Implementing Sortable class My. Integer implements Sortable { My. Integer(int val) {this. val = val; } int get. Val() {return val; } boolean is. Less. Than(Sortable s) { My. Integer mi = (My. Integer)s; return this. val < mi. val; } } private int val;
- Slides: 110