410 likes | 486 Views
Comp 401 Dynamic Dispatch and Abstract Methods. Instructor: Prasun Dewan. A StringHistory Implementation. public class AStringHistory implements StringHistory { public static final int MAX_SIZE = 50; String[] contents = new String[MAX_SIZE]; int size = 0;
E N D
Comp 401Dynamic Dispatch and Abstract Methods Instructor: PrasunDewan
A StringHistory Implementation • public classAStringHistoryimplementsStringHistory { • public staticfinalint MAX_SIZE = 50; • String[] contents = new String[MAX_SIZE]; • int size = 0; • publicint size() { return size;} • public String elementAt (int index) { return contents[index]; } • booleanisFull() { return size == MAX_SIZE; } • publicvoidaddElement(String element) { • if (isFull()) • System.out.println("Adding item to a full history"); • else { • contents[size] = element; • size++; • } • } • }
A Big StringHistory Implementation • public classABigStringHistoryimplementsStringHistory { • public staticfinalint MAX_SIZE = 500; • String[] contents = new String[MAX_SIZE]; • int size = 0; • publicint size() { return size;} • public String elementAt (int index) { return contents[index]; } • booleanisFull() { return size == MAX_SIZE; } • publicvoidaddElement(String element) { • if (isFull()) • System.out.println("Adding item to a full history"); • else { • contents[size] = element; • size++; • } • } • }
Null Overridden Method publicabstractclassAnAbstractStringHistory{ booleanisFull(){}; voiduncheckedAddElement(Object element){}; publicvoidaddElement(Object element) { if (!isFull()) uncheckedAddElement(element); else handleError(); } voidhandleError () { System.out.println("Adding item to a full history"); }; }
A StringHistory Implementation • public classAStringHistoryextends AnAbstractStringHistory • implementsStringHistory { • publicfinalint MAX_SIZE = 50; • String[] contents = new String[MAX_SIZE]; • int size = 0; • publicint size() { return size;} • public String elementAt (int index) { return contents[index]; } • booleanisFull() { return size == MAX_SIZE; } • voiduncheckedAddElement(String element) { • contents[size] = element; • size++; • } • }
Dynamic Dispatch publicabstractclassACollection { booleanisFull(){}; voiduncheckedAddElement(Object element){}; publicvoidaddElement(Object element) { if (!isFull()) uncheckedAddElement(element); else handleError(); } voidhandleError () { System.out.println("Adding item to a full history"); }; } • public classAStringHistoryextends ACollectionimplementsStringHistory { • … • booleanisFull() { return size == MAX_SIZE; } • voiduncheckedAddElement(String element) { • contents[size] = element; • size++; • } • } Which isFull() called when addElement is invoked on an instance of AStringHistory()? Regular method: called method “statically dispatched” at compile time, so overridden isFull() Virtual method: most specific implementation of called method “dynamically dispatched” at execution time, so overriding isFull()
Regular vs. Virtual Method • Smalltalk: had only virtual methods • With each object a table kept which associates method header with most specific implementation. • Table accessed at execution time. • C++: Had both virtual and regular methods • More efficient with regular methods • Java: Has only virtual methods • Abstract and interface method by definition are virtual methods
Correct A Big StringHistory Implementation • public classABigStringHistoryextends AnAbstractStringHistory • implementsStringHistory { • publicfinalint MAX_SIZE = 500; • String[] contents = new String[MAX_SIZE]; • int size = 0; • publicint size() { return size;} • public String elementAt (int index) { return contents[index]; } • booleanisFull() { return size == MAX_SIZE; } • voiduncheckedAddElement(String element) { • contents[size] = element; • size++; • } • }
An Erroneous Big StringHistory Implementation • public classABigStringHistoryextends AnAbstractStringHistory • implementsStringHistory { • publicfinalint MAX_SIZE = 500; • String[] contents = new String[MAX_SIZE]; • int size = 0; • publicint size() { return size;} • public String elementAt (int index) { return contents[index]; } • booleanisFull() { return size == MAX_SIZE; } • //voiduncheckedAddElement(String element) { • // contents[size] = element; • // size++; • //} • }
Null Overridden Method publicabstractclassAnAbstractStringHistory{ booleanisFull(){}; voiduncheckedAddElement(Object element){}; publicvoidaddElement(Object element) { if (!isFull()) uncheckedAddElement(element); else handleError(); } voidhandleError () { System.out.println("Adding item to a full history"); }; }
abstract methods publicabstractclassAnAbstractStringHistory{ abstractbooleanisFull(); abstractvoiduncheckedAddElement(Object element) publicvoidaddElement(Object element) { if (!isFull()) uncheckedAddElement(element); else handleError(); } voidhandleError () { System.out.println("Adding item to a full history"); }; } Only header of method provided as in interface
An Erroneous Big StringHistory Implementation • public classABigStringHistoryimplementsStringHistory { • publicfinalint MAX_SIZE = 500; • String[] contents = new String[MAX_SIZE]; • int size = 0; • publicint size() { return size;} • public String elementAt (int index) { return contents[index]; } • booleanisFull() { return size == MAX_SIZE; } • //voiduncheckedAddElement(String element) { • // contents[size] = element; • // size++; • //} • } Java complains abstract method not implemented in concrete class
Null vs. Abstract Methods publicabstractclassAnAbstractStringHistory{ booleanisFull() {} voiduncheckedAddElement(Object element) {} publicvoidaddElement(Object element) { if (!isFull()) uncheckedAddElement(element); else handleError(); } voidhandleError () { System.out.println("Adding item to a full history"); }; } Null methods could replace abstract methods in this example. Abstract method force overriding implementations in subclasses. Provide better documentation, indicating to programmer that subclass will implement them.
Abstract method • Declared only in abstract classes • Keyword: abstract • No body • Each (direct or indirect) subclass must implement abstract methods defined by an abstract class. • Much like each class must implement the methods defined by its interface(s).
Abstract class vs. methods • Abstract method => containing class abstract • Cannot have an unimplemented method in an instance • Abstract class may not contain abstract method
Abstract Class publicabstractclassAnAbstractStringHistory{ abstractbooleanisFull(); abstractvoiduncheckedAddElement(Object element); publicvoidaddElement(Object element) { if (!isFull()) uncheckedAddElement(element); else handleError(); } voidhandleError () { System.out.println("Adding item to a full history"); }; }
Concrete Class • public classABigStringHistoryextends AnAbstractStringHistory • implementsStringHistory { • publicstaticfinalint MAX_SIZE = 500; • String[] contents = new String[MAX_SIZE]; • int size = 0; • publicint size() { return size;} • public String elementAt (int index) { return contents[index]; } • booleanisFull() { return size == MAX_SIZE; } • voiduncheckedAddElement(String element) { • contents[size] = element; • size++; • } • } Move more code to abstract class?
New Abstract Class • public classAnAbstractStringHistoryimplementsStringHistory { • String[] contents = new String[maxSize()]; • int size = 0; • abstractintmaxSize(); • publicint size() { return size;} • public String elementAt (int index) { return contents[index]; } • booleanisFull() { return size == maxSize(); } • publicvoidaddElement(String element) { • if (isFull()) • System.out.println("Adding item to a full history"); • else { • contents[size] = element; • size++; • } • } • }
New Concrete Class • public classABigStringHistoryextends AnAbstractStringHistory • implementsStringHistory { • publicstaticfinalint MAX_SIZE = 500; • intmaxSize() { • return MAX_SIZE; • } • } Method call needed to access constant
Another Course package courses; publicabstractclassACourse { String title, dept; publicACourse (String theTitle, String theDept) { super(); title = theTitle; dept = theDept; } public String getTitle() { return title; } publicString getDepartment() { return dept; } abstractpublicintgetNumber(); } Abstract Method
Alternative ARegularCourse package courses; publicclassARegularCourseextendsACourse { intcourseNum; publicARegularCourse (String theTitle, String theDept, inttheCourseNum) { super (theTitle, theDept); courseNum = theCourseNum; } publicintgetNumber() { returncourseNum; } } Like interface, force implementation of getNumber() Implementation of abstract method
Alternative AFreshmanSeminar package courses; publicclassAFreshmanSeminarextendsACourse { publicAFreshmanSeminar (String theTitle, String theDept) { super (theTitle, theDept); } publicintgetNumber() { return SEMINAR_NUMBER; } } Implementation of abstract method
Abstract Class publicabstractclassAnAbstractStringHistory{ abstractbooleanisFull(); abstractvoiduncheckedAddElement(Object element); publicvoidaddElement(Object element) { if (!isFull()) uncheckedAddElement(element); else handleError(); } voidhandleError () { System.out.println("Adding item to a full history"); }; } Not public
Factory Abstract Method … abstract <T> <M>(…) ; … extends … <T> <M>(…) { returnnew <C1> (…) } … … <T> <M> (…){ returnnew <C2> (…) } … Abstract method that returns an instance of some type T – like a factory, it creates and initializes an object.
Abstract methods vs. Interfaces • Unimplemented methods become abstract methods to be implemented by concrete subclasses. Do not need explicit implements clause in subclass if no additional public methods implemented, • publicclassARegularCourseextendsACourse { …. } • A class with only abstract methods simulates interfaces. • No multiple inheritance in Java • Separation of abstract and concrete methods with interfaces. • A class implements but does not inherit a specification!
Abstract methods vs. Interfaces • Non-public abstract methods relevant even in Java • All interface methods must be public. • May want non public abstract methods.E.g. • abstractbooleanisFull() ;
Another Course package courses; publicabstractclassACourseimplements Course { String title, dept; publicACourse (String theTitle, String theDept) { super(); title = theTitle; dept = theDept; } public String getTitle() { return title; } publicString getDepartment() { return dept; } } An abstract class can implement any interface This means all sub classes implement the interface Interface implementation check is made for concrete classes
Alternative ARegularCourse package courses; publicclassARegularCourseextendsACourse { intcourseNum; publicARegularCourse (String theTitle, String theDept, inttheCourseNum) { super (theTitle, theDept); courseNum = theCourseNum; } publicintgetNumber() { returncourseNum; } } Saying ARegularCourse implements Course is redundant
Alternative AFreshmanSeminar package courses; publicclassAFreshmanSeminarextendsACourse { publicAFreshmanSeminar (String theTitle, String theDept) { super (theTitle, theDept); } publicintgetNumber() { return SEMINAR_NUMBER; } } No need for implementation clause
Polymorphic method ARegularCourse AFreshmanSeminar getNumber() in ARegularCouse getNumber() in AFreshmanSeminar Dynamically dispatched method Overloaded method Overloading, Polymorphism, and Dynamic dispatch static void print (Course course) { System.out.println( course.getTitle() + " " + course.getDepartment() + course.getNumber() ); } static void print (CourseListcourseList) { … print(course); … }
Overloading, Polymorphism, and Dynamic dispatch • Same method name works on different types. • Overloading • Multiple (static or instance) methods with same name but different parameter types. • Resolved at compile time based on parameter types • Polymorphism • Same method implementation works on multiple object types. • Dynamic dispatch (or virtual methods) • Multiple instance methods with same name in different classes • Resolved at execution time based on type of target object
Polymorphism vs. Overloading and Dynamic Dispatch • Polymorphism vs. Overloading • Polymorphism: single print (Course course) • Overloading: print (ARegularCourse course) and print (AFreshmanSeminar course) with same implementation. • Polymorphism vs. Dynamic Dispatch • Polymorphism: single getTitle() in ACourse • Dynamic dispatch: getTitle() in AFreshmanSeminar() and getTitle() in ARegularCourse() with same implementation. • Create polymorphic code when you can • In overloading and dynamic dispatch, multiple implementations associated with each method name. • In polymorphic case, single implementation. • Use interfaces rather than classes as types of parameters • Use supertypes rather than subtypes as types of parameters
Polymorphism vs. Overloading and Dynamic Dispatch • Cannot always create polymorphic method. • getNumber() for ARegularCourse and AFreshmanSeminar do different things. • print(Course course) and print (CourseListcourseList) do different things. • When polymorphism not possible try overloading and dynamic dispatch.
Manualinstead of Dynamic Dispatch staticvoid print (Course course) { if (course instanceofARegularCourse) number = ((ARegularCourse)course).getCourseNumberRegularCourse (); else if (course instanceofAFreshmanSeminar) number = ((AFreshmanSeminar)course).getCourseNumberFreshmanSeminar (); System.out.println( course.getTitle() + " " + course.getDepartment() + number ); } Must update code as subtypes added More Work
Can we get rid of manual dispatch/instanceof? staticvoid print (Course course) { if (course instanceofRegularCourse) System.out.print(“Regular Course: ”); elseif (course instanceofAFreshmanSeminar) System.out.println(“Freshman Seminar”); System.out.println( course.getTitle() + " " + course.getDepartment() + course.getNumber() ); }
Cannot always get rid of manual dispatch/instancof staticvoid print (Course course) { printHeader (course); System.out.println( course.getTitle() + " " + course.getDepartment() + course.getNumbert() ); } staticvoidprintHeader (ARegularCourse course) { System.out.print(“Regular Course: ”); } staticvoidprintHeader (AFreshmanSeminar course) { System.out.print(“Freshman Seminar: ”); } Actual parameter cannot be supertype of formal parameter At compile time, do not know if regular course or freshman seminar
printHeader() in ARegularCourse package courses; publicclassARegularCourseextendsACourseimplements Course { … publicvoidprintHeader () { System.out.print (“Regular Course: ”) } }
printHeader() in AFreshmanSeminar package courses; publicclassAFreshmanSeminarextendsACourse implements FreshmanSeminar { … publicvoidprintHeader () { System.out.print (“Freshman Seminar: ”) } }
Should not always get rid of manual dispatch/instancof staticvoid print (Course course) { printHeader (course); System.out.println( course.getTitle() + " " + course.getDepartment() + course.getNumbert() ); } staticvoidprintHeader (ARegularCourse course) { System.out.print(“Regular Course: ”); } staticvoidprintHeader (AFreshmanSeminar course) { System.out.print(“Freshman Seminar: ”); } Should not put printHeader() in ARegularCourse or AFreshmanSeminar as it is UI code. Hence need to use instanceof Used in parsers