360 likes | 536 Views
Chap. 2. Object-Oriented Design. Inheritance. Building. Apartment. House. Commercial Building. Low-rise Apartment. High-rise Apartment. Two-story House. Ranch. Skyscraper. An example of an “is a” hierarchy involving architectural buildings. class: S. fields: x.
E N D
Inheritance Building Apartment House Commercial Building Low-rise Apartment High-rise Apartment Two-story House Ranch Skyscraper An example of an “is a” hierarchy involving architectural buildings
class: S fields: x methods: a() b() c() extends class: T fields: y methods: d() e()
Polymorphism • Polymorphism refers to the ability of an object variable to take different forms. • If both S and T define an a() method, when o refers to an object from class T, it uses T’s a() when o.a() is called; when o refers to an object from class S, then it uses S’s a() when o.a() is called. • Thus, the object o is polymorphic.
Overloading • Overloading is a useful technique related to polymorphism. • Overloading occurs when a single class T has multiple methods with the same name, but different signature. • The signature of a method is a combination of its name and the type and number of arguments that are passed to it.
For example, suppose a class T, which defines a method a(), extends a class U, which defines a method a(x,y). • If an object o from class T receives the message “o.a(x,y)”, then U’s version of method a is invoked. • Inheritance, polymorphism, and method overloading support the development of reusable software. • In Java, each class can extend exactly one other class.
Java uses two kinds of method overriding, refinement and replacement. • In the replacement type of overriding, a method completely replaces the method of the superclass that it is overriding. • In the refinement type of overriding, a method does not replace the method of its superclass, but instead adds additional code to that of its superclass. • In Java, constructors use the refinement type of method overriding whereas regular methods use replacement.
The Keyword this this is used as reference to the current instance of a class. It is useful, for example, when we want to pass the current object as a parameter to some method. public class ThisTester { // instance variable public int dog = 2; // constructor ThisTester( ) { // null constructor } public void clobber( ) { double dog = 5.0; this.dog = (int) dog; // two different dogs! }
public static void main(String args[]) { ThisTester t = new ThisTester(); System.out.println(“The dog field = “+ t.dog); t.clobber(); System.out.println(“After clobbering, dog = “ + t.dog); } } Output: The dog field = 2 After clobbering, dog = 5
An example to pass arguments to methods class Car { String licensePlate = ""; // e.g. "New York 543 A23" double speed = 0.0; // in kilometers per hour double maxSpeed = 120.0; // in kilometers per hour // accelerate to maximum speed // put the pedal to the metal void floorIt() { this.speed = this.maxSpeed; } void accelerate(double deltaV) { this.speed = this.speed + deltaV; if (this.speed > this.maxSpeed) { this.speed = this.maxSpeed; } if (this.speed < 0.0) { this.speed = 0.0; } } }
class: Progression fields: long first long cur methods: Progression( ) long firstValue() long nextValue() void printProgession(int) extends extends extends class: GeomProgression class: FibonacciProgression class: ArithProgression fields: fields: long prev fields: long inc methods: FibonacciProgression( ) FibonacciProgression(long, long) long nextValue( ) methods: GeomProgression( ) GeomProgression(long) long nextValue() methods: ArithProgression( ) ArithProgression(long) long nextValue()
Inheritance Example A generic class for numeric progressions public class Progression { // First value of the progression. protected long first; // Current value of the progression. protected long cur; // Default constructor Progression() { cur = first = 0; }
// Resets the progression to the first value protected long firstValue( ) { cur = first; return cur; } // Advances the progression to the next value. protected long nextValue( ) { return ++cur; // default next value } // Prints the first n values of the progression public void printProgression(int n) { System.out.print(firstValue( )); for (int i=2; i<= n; i++) System.out.print(“ “+nextValue()); System.out.println( ); // ends the line } }
Inheritance Example An Arithmetic Progression class class ArithProgression extends Progression { // Increment protected long inc; // Inherits variables first and cur // Default constructor setting a unit increment ArithProgression( ) { this(1); } // Parametric constructor providing the increment ArithProgression(long increment) { inc = increment; } // Advances the progression by adding the increment to the current value protected long nextValue( ) { cur += inc; return cur; } // Inherits methods firstValue( ) and printProgression(int) }
Inheritance Example A Geometric Progression class class GeomProgression extends Progression { // Inherits variables first and cur // Default constructor setting base 2 GeomProgression( ) { this(2); } // Parametric constructor providing the base GeomProgression(long base) { first = base; cur = first; } // advances the progression by multiplying the base with the current value protected long nextValue( ) { cur *= first; return cur; } // Inherits methods firstValue( ) and printProgression(int) }
Inheritance Example A Fibonacci Progression class class FibonacciProgression extends Progression { // Previous value long prev; // Inherits variables first and cur FibonacciProgression( ) { this(0,1); } // Parametric constructor providing the first and the second values FibonacciProgression(long value1, long value2) { first = value1; prev = value2 – value1; // fictitious value preceding the first } // Advances the progression by adding the previous value to the current value protected long nextValue( ) { long temp = prev; prev = cur; cur += temp; return cur; } // Inherits methods firstValue( ) and printProgression(int) }
// Test program for the progression classes class Tester { public static void main (String[ ] args) { Progression prog; // test ArithProgression System.out.println(“Arithmetic progression with default increment:”); prog = new ArithProgression( ); prog.printProgression(10); System.out.println(“Arithmetic progression with increment 5:”); prog = new ArithProgression(5); prog.printProgression(10); // test GeomProgression System.out.println(“Geometric progression with default base:”); prog = new GeomProgression( ); prog.printProgression(10); System.out.println(“Geometric progression with base 3:”); prog = new GeomProgression(3); prog.printProgression(10); // test FibonacciProgression System.out.println(“Fibonacci progression with default start values:”); prog = new FibonacciProgression( ); prog.printProgression(10); System.out.println(“Fibonacci progression with start values 4 and 6:”); prog = new FibonacciProgression(4,6); prog.printProgression(10); } }
Exceptions • In Java, exceptions are objects that are “thrown” by code that encounters some sort of unexpected condition. • They can also be thrown by the Java run-time environment should it encounter an unexpected condition, like running out of object memory.
A throw statement is typically written as: throw new <exception_constructor>([<param>,<param>,…]); e.g.: if (insertIndex > size( )) { throw new BoundaryViolationException(“No element at index “ + insertIndex); e.g.: // can specify all the exceptions that might be thrown by a method public void goShopping( ) throws ShoppingListTooSmallException, OutOfMoneyException { // method body … }
Catching Exceptions The general syntax for a try-catch block in Java is as follows: try <block_of_statements_1> catch (<exception_type> <identifier>) <block_of_statements_2> [catch (<exception_type> <identifier>) <block_of_statements_3>] … [finally <block_of_statements_n>]
E.g.: int index = Integer.MAX_VALUE; //2.14 Billion try // This code might have a problem … { String toBuy = shoppingList[index]; } catch (ArrayIndexOutOfBoundsException aioobx) { System.out.println(“The index “+index+” is outside the array.”); } E.g.: catch (ArrayIndexOutOfBoundsException aioobx) { throw new ShoppingListTooSmallException( “Product index is not in the shopping list”); }
Interface • An interface is a collection of method declarations with no data and no bodies. • When a class implements an interface, it must implement all of the methods declared in the interface. • The compiler or run-time system requires that the types of parameters that are actually passed to methods rigidly conform with the type specified in the interface. This requirement is known as strong typing.
Example // Interface for objects that can be sold public interface Sellable { // description of the object public String description(); // list price in cents public int listPrice(); // lowest price in cents we will accept public int lowestPrice(); }
// class for photographs that can be sold public class Photograph implements Sellable { private String descript; // description of this photo private int price; // the price we are setting private boolean color; // true if photo is in color public Photograph(String desc, int p, boolean c) { // constructor descript = desc; price = p; color = c; } public String description() { return descript; } public int listPrice() { return price; } public int lowestPrice() { return price/2; } public boolean isColor() { return color; } }
// Interface for objects that can be transported public interface Transportable { // weight in grams public int weight(); // whether the object is hazardous public boolean isHazardous(); }
// Class for objects that can be sold, packed, and shipped public class BoxedItem implements Sellable, Transportable { private String descript; // description of this item private int price; // list price in cents private int weight; // weight in grams private boolean haz; // true if object is hazardous private int height=0; // box height in centimeters private int width=0; // box width in centimeters private int depth=0; // box depth in centimeters // constructors public BoxedItem (String desc, int p, int w, boolean h) { descript = desc; price = p; weight = w; haz = h; } public String description() { return descript; } public int listPrice() { return price; } public int lowestPrice() {return price/2;} public int weight() { return weight; } public boolean isHazardous() {return haz;} public int insuredValue() {return price*2;} public void setBox(int h, int w, int d) { height = h; width = w; depth = d; } }
Multiple Inheritance in Interfaces In Java, multiple inheritance is allowed for interfaces but not for classes. e.g.: public interface InsurableItem extends Transportable, Sellable { public int insuredValue(); // return insured Value in cents } public class BoxedItem implements InsurableItem { // … rest of code exactly as before }
Abstract Classes • An abstract class is a class that contains empty method declarations (that is, declarations of methods without bodies) as well as concrete definitions of methods and/or instance variables. • An abstract class lies between an interface and a complete concrete class. E.g.: • A concrete number class, such as java.lang.Integer and java.lang.Double, extends the java.lang.Number class and fills in the details for the abstract methods of the superclass.
Casting in an Inheritance Hierarchy Number n; Integer i; n = new Integer(3); i = (Integer)n; // This is legal n = new Double(3.1415); i = (Integer) n; // This is illegal
Number n; Integer i; n = new Integer(3); if (n istanceof Integer) i = (Integer) n; // This is legal n = new Double(3.1415); if ( n instanceof Integer) i = (Integer) n; // This will not be attempted
Casting with Interface public interface Person { public boolean equalTo (Person other); // is this the same person public String getName( ); // get this person’s name public int getAge( ); // get this person’s age }
public class Student implements Person { String id; String name; int age; public Student (String i, String n, int a) {// simple constructor id = i; name = n; age = a; } protected int studyHours( ) { return age/2;} // just a guess public String getID( ) { return id; } // ID of the student public String getName( ) { return name;} // from Person interface public int getAge( ) { return age; }// from Person interface public boolean equalTo(Person other) { // from Person interface Student otherStudent = (Student) other; // cast Person to Student return (id.equals(otherStudent.getID( ))); // compare IDs } }
public class PersonPairDirectory { // … instance variables would go here … public PersonPairDirectory() {//default constructor goes here} public void insert (Person person, Person other) { // insert code goes here } public Person findOther (Person person) { return null; } // stub for find public void remove (Person person, Person other) { // remove code goes here } } Student cute_one = myDirectory.findOther(smart_one); // wrong! Student cute_one = (Student) (myDirectory.findOther(smart_one));
Recursion 1 if n=0 Factorial(n)= n*factorial(n-1) if n>=1. public static int recursiveFactorial(int n) { // recursive factorial function if (n==0) return 1; // base case else return n*recursiveFactorial(n-1); // recursive case }
The Adapter Pattern • The adapter pattern applies to any context where we want to modify an existing class so that its methods match those of a related, but different, class or interface. • One general way for applying the adapter pattern is to define the new class in such a way that it contains an instance of the old class as a hidden field, and implement each method of the new class using methods of this hidden instance variable.
class StudentPairDirectory adapting class PersonPairDirectory // Class for a directory storing pairs of Student objects public class StudentPairDirectory { protected PersonPairDirectory directory; public StudentPairDirectory() { directory=new PersonPairDirectory();} public void insert(Student s, Student t) {directory.insert(s,t);} public Student findOther(Student s) { return (Student) directory.findOther(s); } public void remove(Student s, Student t) { directory.remove(s,t); } }