Download Object-oriented Programming and more Slides Object Oriented Programming in PDF only on Docsity! CS211, Fall, 1999. KAM OOP.fm 1/71 Object-oriented Programming Reference: Chapter 6 of A Programmer’s Guide to Java Certification: A Comprehensive Primer. CS211, Fall, 1999. KAM Object-oriented Programming 2/71 Overview • The inheritance relationship: is-a • The aggregation relationship: has-a • Overridden and overloaded methods • The keyword super • Variable shadowing • Constructors and constructor chaining using this() and super() • Single implementation inheritance, multiple interface inheritance and supertypes • Assigning, casting and passing references • The instanceof operator • Polymorphism and dynamic method lookup • Encapsulation • Choosing between inheritance and aggregation CS211, Fall, 1999. KAM Object-oriented Programming 3/71 Extensibility by Linear Implementation Inheritance • One fundamental mechanism for code reuse. • The new class inherits all the members of the old class - not to be confused with accessibility of superclass members. • A class in Java can only extend one other class, i.e. it can only have one immediate superclass. • The superclass is specified using the extends clause in the header of the subclass. • The definition of the subclass only specifies the additional new and modified mem- bers in its class definition. • All classes extend the java.lang.Object class. CS211, Fall, 1999. KAM Object-oriented Programming 4/71 Example 1 Extending Classes class Light { // (1) // Instance variables private int noOfWatts; // wattage private boolean indicator; // on or off private String location; // placement // Static variable private static int counter; // no. of Light objects created // Constructor Light() { noOfWatts = 50; indicator = true; location = new String("X"); } // Instance methods public void switchOn() { indicator = true; } public void switchOff() { indicator = false; } public boolean isOn() { return indicator; } // Static methods public static void writeCount() { System.out.println("Number of lights: " + counter); } //... } CS211, Fall, 1999. KAM Object-oriented Programming 9/71 Aggregation • A major mechanism for code reuse mechanism is aggregation. • Aggregation defines the relationship has-a (a.k.a. whole–part relationship) between an instance of a class and its constituents (a.k.a. parts). • In Java, an aggregate object cannot contain other objects. • It can only have references to its constituent objects. • The has-a relationship defines an aggregation hierarchy. • In this simple form of aggregation, constituent objects can be shared between objects, and their lifetimes are independent of the lifetime of the aggregate object. CS211, Fall, 1999. KAM Object-oriented Programming 10/71 Illustrating Inheritance Figure 2 Inheritance Relationship between String and Object classes java.lang.Object equals() getClass() notify() ... java.lang.String equals() substring() length() ... CS211, Fall, 1999. KAM Object-oriented Programming 11/71 Example 2 Illustrating Inheritance // String class is a subclass of Object class class Client { public static void main(String args[]) { String stringRef = new String("Java"); // (1) System.out.println("(2): " + stringRef.getClass()); // (2) System.out.println("(3): " + stringRef.length()); // (3) Object objRef = stringRef; // (4) // System.out.println("(5): " + objRef.length()); // (5) Not OK. System.out.println("(6): " + objRef.equals("Java")); // (6) System.out.println("(7): " + objRef.getClass()); // (7) stringRef = (String) objRef; // (8) System.out.println("(9): " + stringRef.equals("C++")); // (9) } } Output from the program: (2): class java.lang.String (3): 4 (6): true (7): class java.lang.String (9): false CS211, Fall, 1999. KAM Object-oriented Programming 12/71 Inheriting from the Superclass • The subclass String inherits the method getClass() from the superclass Object - this is immaterial for a client of class String System.out.println("(2): " + stringRef.getClass()); // (2) Extending the Superclass • The subclass String defines the method length(), which is not in the superclass Object, thereby extending the superclass. System.out.println("(3): " + stringRef.length()); // (3) Upcasting • A subclass reference can be assigned to a superclass reference, because a subclass object can be used where a superclass object can be used. Object objRef = stringRef; // (4) creates aliases • Methods exclusive to the String subclass cannot be invoked via the superclass reference: System.out.println("(5): " + objRef.length()); // (5) Not OK. CS211, Fall, 1999. KAM Object-oriented Programming 13/71 Method Overriding • The equals() method is redefined in the String class with the same signature (i.e. method name and parameters) and the same return type. System.out.println("(6): " + objRef.equals("Java")); // (6) • The compiler can check that the Object class does define a method called equals(). Polymorphism and Dynamic Method Binding • The ability of a superclass reference to denote objects of its own class and its sub- classes at runtime is called polymorphism. • The method invoked is dependent on the actual (type of) object denoted by the ref- erence at runtime. • The actual method is determined by dynamic method lookup. System.out.println("(6): " + objRef.equals("Java")); // (6) System.out.println("(7): " + objRef.getClass()); // (7) • At (6), dynamic method lookup results in the equals() method from the String class being executed, and not the one in the Object class. CS211, Fall, 1999. KAM Object-oriented Programming 14/71 • At (7), dynamic method lookup determines that the method getClass() inherited from the Object class to be executed - leading to a "search" up the inheritance hier- archy. Downcasting • Casting the value of a superclass reference to a subclass type is called downcasting, and requires explicit casting. stringRef = (String) objRef; // (8) System.out.println("(9): " + stringRef.equals("C++")); // (9) • The cast can be invalid at runtime! • A ClassCastException would be thrown at runtime. • Use the instanceof operator to determine the runtime type of an object before any cast is applied. if (objRef instanceof String) { stringRef = (String) objRef; System.out.println("(9): " + stringRef.length); } CS211, Fall, 1999. KAM Object-oriented Programming 19/71 Output from the program: Large bill: 500.0 Large bill: 500.0 Small bill: 50.0 Large bill Small bill Small bill No bill CS211, Fall, 1999. KAM Object-oriented Programming 20/71 Variable Shadowing • A subclass cannot override variable members of the superclass, but it can shadow them. • A subclass method can use the keyword super to access inherited members, including shadowed variables. • When a method is invoked on an object using a reference, it is the class of the cur- rent object denoted by the reference, not the type of the reference, that determines which method implementation will be executed. • When a variable of an object is accessed using a reference, it is the type of the refer- ence, not the class of the current object denoted by the reference, that determines which variable will actually be accessed. CS211, Fall, 1999. KAM Object-oriented Programming 21/71 Overriding vs. Overloading • Method overriding requires the same method signature (name and parameters) and the same return type, and that the original method is inherited from its super- class. • Overloading requires different method signatures, but the method name should be the same. • To overload methods, the parameters must differ in type or number. • The return type is not a part of the signature, changing it is not enough to overload methods. • A method can be overloaded in the class it is defined in, or in a subclass of its class. • Invoking an overridden method in the superclass from a subclass requires special syntax (for example, the keyword super). CS211, Fall, 1999. KAM Object-oriented Programming 22/71 Object Reference super • The this reference is passed as an implicit parameter when an instance method is invoked. • It denotes the object on which the method is called. • The keyword super can be used in the body of an instance method in a subclass to access variables and invoke methods inherited from the superclass. • The keyword super provides a reference to the current object as an instance of its superclass. • The super.super.X construct is invalid. CS211, Fall, 1999. KAM Object-oriented Programming 23/71 Example 4 Using super Keyword // Exceptions class InvalidHoursException extends Exception {} class NegativeHoursException extends InvalidHoursException {} class ZeroHoursException extends InvalidHoursException {} class Light { protected String billType = "Small bill"; // (1) protected double getBill(int noOfHours) throws InvalidHoursException { // (2) double smallAmount = 10.0, smallBill = smallAmount * noOfHours; System.out.println(billType + ": " + smallBill); return smallBill; } public void banner() { // (3) System.out.println("Let there be light!"); } } CS211, Fall, 1999. KAM Object-oriented Programming 24/71 class TubeLight extends Light { public String billType = "Large bill"; // (4) Shadowing. public double getBill(final int noOfHours) throws ZeroHoursException { // (5) Overriding. double largeAmount = 100.0, largeBill = largeAmount * noOfHours; System.out.println(billType + ": " + largeBill); return largeBill; } public double getBill() { // (6) System.out.println("No bill"); return 0.0; } } class NeonLight extends TubeLight { // ... public void demonstrate() throws InvalidHoursException { // (7) super.banner(); // (8) super.getBill(20); // (9) super.getBill(); // (10) System.out.println(super.billType); // (11) ((Light) this).getBill(20); // (12) System.out.println(((Light) this).billType); // (13) } } CS211, Fall, 1999. KAM Object-oriented Programming 29/71 Example 5 this() Constructor Call class Light { // Instance Variables private int noOfWatts; private boolean indicator; private String location; // Constructors Light() { // (1) Explicit default constructor this(0, false); System.out.println("Returning from default constructor no. 1."); } Light(int watt, boolean ind) { // (2) Non-default this(watt, ind, "X"); System.out.println("Returning from non-default constructor no. 2."); } Light(int noOfWatts, boolean indicator, String location) { // (3) Non-default this.noOfWatts = noOfWatts; this.indicator = indicator; this.location = new String(location); System.out.println("Returning from non-default constructor no. 3."); } } CS211, Fall, 1999. KAM Object-oriented Programming 30/71 public class DemoThisCall { public static void main(String args[]) { // (4) System.out.println("Creating Light object no.1."); Light light1 = new Light(); // (5) System.out.println("Creating Light object no.2."); Light light2 = new Light(250, true); // (6) System.out.println("Creating Light object no.3."); Light light3 = new Light(250, true, "attic"); // (7) } } Output from the program: Creating Light object no.1. Returning from non-default constructor no. 3. Returning from non-default constructor no. 2. Returning from default constructor no. 1. Creating Light object no.2. Returning from non-default constructor no. 3. Returning from non-default constructor no. 2. Creating Light object no.3. Returning from non-default constructor no. 3. CS211, Fall, 1999. KAM Object-oriented Programming 31/71 super() Constructor Call • The super() construct is used in a subclass constructor to invoke constructors in the immediate superclass. • This allows the subclass to influence the initialization of its inherited state when an object of the subclass is created. • A super() call in the constructor of a subclass will result in the execution of the rel- evant constructor from the superclass, based on the arguments passed. • The super() call must occur as the first statement in a constructor, and it can only be used in a constructor definition. • This implies that this() and super() calls cannot both occur in the same con- structor. CS211, Fall, 1999. KAM Object-oriented Programming 32/71 Example 6 super() Constructor Call class Light { // Instance Variables private int noOfWatts; private boolean indicator; private String location; // Constructors Light() { // (1) Explicit default constructor this(0, false); System.out.println( "Returning from default constructor no. 1 in class Light"); } Light(int watt, boolean ind) { // (2) Non-default this(watt, ind, "X"); System.out.println( "Returning from non-default constructor no. 2 in class Light"); } Light(int noOfWatts, boolean indicator, String location) { // (3) Non-default super(); // (4) this.noOfWatts = noOfWatts; this.indicator = indicator; this.location = new String(location); System.out.println( "Returning from non-default constructor no. 3 in class Light"); } } CS211, Fall, 1999. KAM Object-oriented Programming 33/71 class TubeLight extends Light { // Instance variables private int tubeLength; private int colorNo; TubeLight(int tubeLength, int colorNo) { // (5) Non-default this(tubeLength, colorNo, 100, true, "Unknown"); System.out.println( "Returning from non-default constructor no. 1 in class TubeLight"); } TubeLight(int tubeLength, int colorNo, int noOfWatts, boolean indicator, String location) { // (6) Non-default super(noOfWatts, indicator, location); // (7) this.tubeLength = tubeLength; this.colorNo = colorNo; System.out.println( "Returning from non-default constructor no. 2 in class TubeLight"); } } public class Chaining { public static void main(String args[]) { System.out.println("Creating a TubeLight object."); TubeLight tubeLightRef = new TubeLight(20, 5); // (8) } } CS211, Fall, 1999. KAM Object-oriented Programming 34/71 Output from the program: Creating a TubeLight object. Returning from non-default constructor no. 3 in class Light Returning from non-default constructor no. 2 in class TubeLight Returning from non-default constructor no. 1 in class TubeLight CS211, Fall, 1999. KAM Object-oriented Programming 39/71 Interfaces • Java provides interfaces which allow new type names to be introduced and used polymorphically, and also permit multiple interface inheritance. • Interfaces support programming by contract. CS211, Fall, 1999. KAM Object-oriented Programming 40/71 Defining Interfaces • An interface defines a contract by specifying prototypes of methods, and not their implementation. <interface header> { <interface body> } • An interface is abstract by definition and therefore cannot be instantiated. It should also not be declared abstract. • Reference variables of the interface type can be declared. CS211, Fall, 1999. KAM Object-oriented Programming 41/71 Figure 3 Inheritance Relations StackImpl push() pop() ... SafeStackImpl isFull() isEmpty() ... IStack push() pop() ISafeStack isFull() isEmpty() Object «interface» «interface» CS211, Fall, 1999. KAM Object-oriented Programming 42/71 Example 7 Interfaces interface IStack { // (1) void push(Object item); Object pop(); } class StackImpl implements IStack { // (2) protected Object[] stackArray; protected int tos; public StackImpl(int capacity) { stackArray = new Object[capacity]; tos = -1; } public void push(Object item) // (3) { stackArray[++tos] = item; } public Object pop() { // (4) Object objRef = stackArray[tos]; stackArray[tos] = null; tos--; return objRef; } public Object peek() { return stackArray[tos]; } } CS211, Fall, 1999. KAM Object-oriented Programming 43/71 interface ISafeStack extends IStack { // (5) boolean isEmpty(); boolean isFull(); } class SafeStackImpl extends StackImpl implements ISafeStack { // (6) public SafeStackImpl(int capacity) { super(capacity); } public boolean isEmpty() { return tos < 0; } // (7) public boolean isFull() { return tos >= stackArray.length; } // (8) } public class StackUser { public static void main(String args[]) { // (9) SafeStackImpl safeStackRef = new SafeStackImpl(10); StackImpl stackRef = safeStackRef; ISafeStack isafeStackRef = safeStackRef; IStack istackRef = safeStackRef; Object objRef = safeStackRef; safeStackRef.push("Dollars"); // (10) stackRef.push("Kroner"); System.out.println(isafeStackRef.pop()); System.out.println(istackRef.pop()); System.out.println(objRef.getClass()); } } CS211, Fall, 1999. KAM Object-oriented Programming 44/71 Output from the program: Kroner Dollars class SafeStackImpl CS211, Fall, 1999. KAM Object-oriented Programming 49/71 Example 8 Variables in Interfaces interface Constants { double PI = 3.14; String AREA_UNITS = " sq.cm."; String LENGTH_UNITS = " cm."; } public class Client implements Constants { public static void main(String args[]) { double radius = 1.5; System.out.println("Area of circle is " + (PI*radius*radius) + AREA_UNITS); // (1) Direct access. System.out.println("Circumference of circle is " + (2*PI*radius) + Constants.LENGTH_UNITS); // (2) Fully qualified name. } } Output from the program: Area of circle is 7.0649999999999995 sq.cm. Circumference of circle is 9.42 cm. CS211, Fall, 1999. KAM Object-oriented Programming 50/71 Types in Java • Only primitive data and reference values can be stored in variables. • Arrays are objects in Java. • Array types (boolean[], Object[], StackImpl[]) implicitly augment the inheritance hierarchy. • All array types implicitly extend the Object class • Note the difference between arrays of primitive datatypes and class types. • Arrays of reference types also extend the array type Object[]. • Variables of array reference types can be declared, and arrays of reference types can be instantiated. • An array reference exhibits the same polymorphic behavior as any other refer- ence, subject to its location in the extended inheritance hierarchy. Corresponding Types: Primitive data values Primitive datatypes. Reference values Class, interface or array type (called reference types). Objects Class or array type. CS211, Fall, 1999. KAM Object-oriented Programming 51/71 Figure 4 Array Types in Inheritance Hierarchy ISafeStack[] IStack[] Object Object[]double[]boolean[] SafeStackImpl[] StackImpl[] ... «interface» «interface» CS211, Fall, 1999. KAM Object-oriented Programming 52/71 Assigning, Passing and Casting References • Reference values, like primitive values, can be assigned, cast and passed as argu- ments. • For values of the primitive datatypes and reference types, conversions occur dur- ing: • Assignment • Parameter passing • Explicit casting • The rule of thumb for the primitive datatypes is that widening conversions are permitted, but narrowing conversions require an explicit cast. • The rule of thumb for reference values is that conversions up the inheritance hier- archy are permitted (called upcasting), but conversions down the hierarchy require explicit casting (called downcasting). • The parameter passing conversion rules are useful in creating generic data types which can handle objects of arbitrary types. CS211, Fall, 1999. KAM Object-oriented Programming 53/71 Example 9 Assigning and Passing Reference Values interface IStack { /* See Example 7 for definition */ } class StackImpl implements IStack { /* See Example 7 for definition */ } interface ISafeStack extends IStack { /* See Example 7 for definition */ } class SafeStackImpl extends StackImpl implements ISafeStack { /* See Example 7 for definition */ } public class ReferenceConversion { public static void main(String args[]) { Object objRef; StackImpl stackRef; SafeStackImpl safeStackRef = new SafeStackImpl(10); IStack iStackRef; ISafeStack iSafeStackRef; // SourceType is a class type objRef = safeStackRef; // (1) Always possible stackRef = safeStackRef; // (2) Subclass to superclass assignment iStackRef = stackRef; // (3) StackImpl implements IStack iSafeStackRef = safeStackRef;// (4) SafeStackImpl implements ISafeStack // SourceType is an interface type objRef = iStackRef; // (5) Always possible iStackRef = iSafeStackRef; // (6) Sub- to super-interface assignment CS211, Fall, 1999. KAM Object-oriented Programming 54/71 // SourceType is an array type. Object[] objArray = new Object[3]; StackImpl[] stackArray = new StackImpl[3]; SafeStackImpl[] safeStackArray = new SafeStackImpl[5]; ISafeStack[] iSafeStackArray = new SafeStackImpl[5]; int[] intArray = new int[10]; objRef = objArray; // (7) Always possible objRef = stackArray; // (8) Always possible objArray = stackArray; // (9) Always possible objArray = iSafeStackArray; // (10) Always possible objRef = intArray; // (11) Always possible // objArray = intArray; // (12) Compile time error stackArray = safeStackArray; // (13) Subclass array to superclass array iSafeStackArray = safeStackArray; // (14) SafeStackImpl implements ISafeStack // Parameter Conversion System.out.println("First call:"); sendParams(stackRef, safeStackRef, iStackRef, safeStackArray,iSafeStackArray); // (15) // Call Signature: sendParams(StackImpl, SafeStackImpl, IStack, // SafeStackImpl[], ISafeStack[]); System.out.println("Second call:"); sendParams(iSafeStackArray, stackRef, iSafeStackRef, stackArray, safeStackArray); // (16) // Call Signature: sendParams(ISafeStack[], StackImpl, ISafeStack, // StackImpl[], SafeStackImpl[]); } CS211, Fall, 1999. KAM Object-oriented Programming 59/71 Polymorphism and Dynamic Method Lookup • Which object a reference will actually denote during runtime cannot always be determined at compile time. • Polymorphism allows a reference to denote different objects in the inheritance hierarchy at different times during execution. • Such a reference is a supertype reference. • When a method is invoked using a reference, the method definition which actu- ally gets executed is determined both by the class of the object denoted by the ref- erence at runtime and the method signature. • Dynamic method lookup is the process of determining which method definition a method signature denotes during runtime, based on the class of the object. • Polymorphism and dynamic method lookup form a powerful programming par- adigm which simplifies client definitions, encourages object decoupling and sup- ports dynamically changing relationships between objects at runtime. CS211, Fall, 1999. KAM Object-oriented Programming 60/71 Figure 5 Polymorphic Methods draw() Shape draw() Rectangle draw() Square draw() Circle draw() IDrawable draw() Map «interface» CS211, Fall, 1999. KAM Object-oriented Programming 61/71 Example 11 Polymorphism and Dynamic Method Lookup interface IDrawable { void draw(); } class Shape implements IDrawable { public void draw() { System.out.println("Drawing a Shape."); } } class Circle extends Shape { public void draw() { System.out.println("Drawing a Circle."); } } class Rectangle extends Shape { public void draw() { System.out.println("Drawing a Rectangle."); } } class Square extends Rectangle { public void draw() { System.out.println("Drawing a Square."); } } class Map implements IDrawable { public void draw() { System.out.println("Drawing a Map."); } } CS211, Fall, 1999. KAM Object-oriented Programming 62/71 public class PolymorphRefs { public static void main(String args[]) { Shape[] shapes = {new Circle(), new Rectangle(), new Square()}; // (1) IDrawable[] drawables = {new Shape(), new Rectangle(), new Map()};// (2) System.out.println("Draw shapes:"); for (int i = 0; i < shapes.length; i++) // (3) shapes[i].draw(); System.out.println("Draw drawables:"); for (int i = 0; i < drawables.length; i++) // (4) drawables[i].draw(); } } Output from the program: Draw shapes: Drawing a Circle. Drawing a Rectangle. Drawing a Square. Draw drawables: Drawing a Shape. Drawing a Rectangle. Drawing a Map. CS211, Fall, 1999. KAM Object-oriented Programming 63/71 Choosing between Inheritance and Aggregation • Choosing between inheritance and aggregation to model relationships can be a crucial design decision. • A good design strategy advocates that inheritance should be used only if the rela- tionship is-a is unequivocally maintained throughout the lifetime of the objects involved, otherwise aggregation is the best choice. • A role is often confused with an is-a relationship. • Changing roles would involve a new object to represent the new role every time this happened. • Code reuse is also best achieved by aggregation when there is no is-a relationship. • Aggregation with method delegating can result in robust abstractions. • Both inheritance and aggregation promote encapsulation of implementation, as changes to the implementation are localized to the class. • Changing the contract of a superclass can have consequences for the subclasses (called the ripple effect) and also for clients who are dependent on a particular behavior of the subclasses. CS211, Fall, 1999. KAM Object-oriented Programming 64/71 Achieving Polymorphism • Polymorphism is achieved through inheritance and interface implementation. • Code relying on polymorphic behavior will still work without any change if new subclasses or new classes implementing the interface are added. • If no obvious is-a relationship is present, then polymorphism is best achieved by using aggregation with interface implementation. CS211, Fall, 1999. KAM Object-oriented Programming 69/71 public class Client { // (5) public static void main(String args[]) { String string1 = "Queues are boring to stand in!"; int length1 = string1.length(); QueueByAggregation queue = new QueueByAggregation(); for (int i = 0; i<length1; i++) queue.enqueue(new Character(string1.charAt(i))); while (!queue.empty()) System.out.print((Character) queue.dequeue()); System.out.println(); String string2 = "!no tis ot nuf era skcatS"; int length2 = string2.length(); StackByInheritance stack = new StackByInheritance(); for (int i = 0; i<length2; i++) stack.push(new Character(string2.charAt(i))); stack.insertAtBack(new Character(’!’)); // (6) while (!stack.empty()) System.out.print((Character) stack.pop()); System.out.println(); } } Output from the program: Queues are boring to stand in! Stacks are fun to sit on! CS211, Fall, 1999. KAM Object-oriented Programming 70/71 Encapsulation • Encapsulation helps to make clear the distinction between an object’s contract and implementation. • Encapsulation has major consequences for program development. • Results in programs that are "black boxes". • Implementation of an object can change without implications for the clients. • Reduces dependency between program modules (hence complexity), as the internals of an object are hidden from the clients, who cannot influence its implementation. • Encourages code-reuse. CS211, Fall, 1999. KAM Object-oriented Programming 71/71 Encapsulation Levels Packages Classes MethodsData BlocksData Data