240 likes | 258 Views
This lecture covers interface inheritance, behavioral subtyping, generics and inheritance, implementation inheritance, class hierarchy, object class methods, class and interface hierarchies, and constructing new instances.
E N D
Lot More Inheritance and Intro to Design Patterns Lecture 12
Recall: Interface Inheritance void select (Person p) { //declared type of p is: //dynamic type of p is: Person SmartPerson Every student is a person Student OsuStudent person extends student implements
Recall: Behavioral Subtyping • A Student can do everything a Person can do • Everywhere a Person is expected, a Student can be used instead void select (Person p) { if (p.getAge() > 18) { p.summons(Date trialDate); ... etc ... • Every method promised in Person interface: • Is implemented in SmartPerson class • Is promised in Student interface • Is implemented in OsuStudent class • Are two separate implementations of getAge really necessary (or even a good idea)?
Generics and Inheritance • Is a Stack<Student> a Stack<Person>? • Can a Stack<Student> do everything a Stack<Person> can do? • Can code expecting a Stack<Person> be given a Stack<Student> instead? • Java’s choice: • No! • For a generic class G, there is no implicit subtyping relationship between G<A> and G<B> • Neither covariance nor contravariance • Regardless of any subtyping relationship between A and B
Implementation Inheritance • Keyword: extends public class OsuStudent extends SmartPerson { . . . } • OsuStudent has SmartPerson’s members (fields + methods, including implementation) • If omitted, java.lang.Object is implicit Person extends SmartPerson implements Student OsuStudent
Inheritance is transitive Every class inherits from java.lang.Object Class Hierarchy Object DnaCreature Parent Base Super SmartPerson Child Derived Sub OsuStudent OsuFaculty extends CseMajor CseGrad
java.lang.Object • The root of all class hierarchies • This is a class called “Object” • There is also a class in java.lang called “Class”! • Provides several useful methods • getClass() • Returns Class of the object instance • String toString() • Returns String representing object value • boolean equals(Object) • Returns true iff argument is equal to object • int hashCode() • Returns an int “hash value” for object • Object clone() • Creates and returns a copy (here be dragons…)
The getClass() Method • Returns an instance of java.lang.Class • Generic class: Class<T> • String getName() • Name of the class as a string, eg “CseMajor” • Think of it as representing the object’s class Student s1 = new OsuStudent(); Student s2 = new CseMajor(); System.out.println(s1.getClass().getName()); System.out.println(s2.getClass().getName()); if (s1.getClass() == s2.getClass()) { . . . • Of course (?) java.lang.Class extends Object! • Try not to think about this too hard
Good Practice: Core Methods • Always override toString() • Default implementation gives class name + @ + a meaningless hex number • eg “BankAccount@3d4606bf” • Always override equals() • Default implementation checks object references for equality Pencil p1 = new LeadedPencil(); Pencil p2 = new LeadedPencil(); assert(!p1.equals(p2)); • Always override hashCode() • Default implementation is memory address • What is a hashCode? Stay tuned... • clone(): To override or not? • Things get very dicey here
Object SmartPerson OsuStudent OsuFaculty CseMajor CseGrad implements extends Class and Interface Hierarchies Voter Salaried Voter v = new SmartPerson(); v = new OsuStudent(); v = new CseGrad(); v = new OsuFaculty();
Mechanics • A class extends exactly one other class • “single inheritance” (cf C++) • A subclass has all the members of its superclass! • Not the private members • Not the constructors (ie just fields and methods) • Subclass can add new members (hence “extends”) • New fields and new methods • Defines its own constructor(s) • Subclass can modify inherited methods • Changes behavior • “overriding”
class SmartPerson { private String name; SmartPerson () { this.name = “Baby Doe”; } SmartPerson (String name) { this.name = name; } void rename (String name) { this.name = name; } String getName () { return this.name; } } class OsuStudent extends SmartPerson { private int ID; OsuStudent () { this.ID = 0; } OsuStudent (String name, int ID) { super(name); this.ID = ID; } boolean winsTicketLottery () { return (this.ID % 2 == 0); } String toString () { return this.getName() + “ [“ + this.ID + “]”; } } Example: Code
name name winsTicket…() toString() getName() getName() rename() rename() ID SmartPerson() SmartPerson() OsuStudent() Example: Graphical View SmartPerson p = new SmartPerson() OsuStudent s = new OsuStudent()
Constructing New Instances • Members of OsuStudent: • its own: ID, winsTicketLottery(), toString() • its parent’s: rename(), getName() • its parent’s parent’s: see java.lang.Object • eg clone(), equals(), hashCode(),… • When a new instance is created: • First, the parent’s constructor is invoked • Can be done explicitly with super() • Otherwise, parent’s default constructor is called • Next, any initialization blocks are executed • Finally, the child’s constructor is executed
Overriding Methods • Overriding: a subclass declares a method that is already present in its superclass • Note: signatures must match (otherwise it is just overloading) class SmartPerson { String toString() { return this.getName(); } } class OsuStudent extends SmartPerson { String toString() { return this.getName() + “ [“ + this.ID + “]”; } } • Question: which method is called? SmartPerson p = new OsuStudent(); System.out.println(p.toString()); • Declared type: SmartPerson, dynamic type: OsuStudent
Polymorphism • Answer: The dynamic type determines which method is called SmartPerson p = new OsuSudent(); p.toString() //calls OsuStudent version • Informal model: • Method invocation is a run-time message to the object • That (run-time) object receives the request, performs the action, and returns the result • Goal: we get the right behavior regardless of actual (ie run-time, ie dynamic) type Person[] csePeople = … //students & faculty in CSE for (int i = 0; i < csePeople[].length; i++) { csePeople[i].toString(); } • Note: this applies to methods only, not fields • Fields can not be overridden, only hidden
Good Practice: @Override • Use @Override annotation with all methods intended to override a method in a superclass class OsuStudent extends SmartPerson { @Override String getName() { . . . } } • Compiler complains if there is no matching method in superclass • Prevents accidental overloading if a mistake is made in the signature • Beware: Differences between Java 5 & 6
Hook methods • Dynamic type of this controls which method executes • Hook method: Called internally, intended to be overridden class Course { void enroll(Student s) { if (checkEligibility(s)) { … } } boolean checkEligibility(Student s) { //determines whether s has prereqs for this course } } class Tutorial extends Course { boolean checkEligibility(Student s) { //determines whether s has paid } } • Yo-yo problem: • Must trace up & down class hierarchy to understand code Course workshop = new Tutorial(); workshop.enroll(s);
Hook and Template Pattern • Recall pattern: • Base class contains both template and hook methods • Template method calls this.hook method • Hook methods are overridden in derived classes • Template method is not • To support this pattern: • Template method is declared final • Hook methods are declared abstract • So base class declared abstract too • Hook methods are declared protected • See divide-and-conquer example • solve() is the template method
Hook and Template, and Abstract Classes public abstract class Course { public final void enroll(Student s) { if (checkEligibility(s)) { … } } protected abstract boolean checkEligibility(Student s); } public class Tutorial extends Course{ @Override protected boolean checkEligibility(Student s) { //determines whether s has paid } }
Protected • We have seen three levels of visibility • private: concrete representation • default (ie package): trusted and co-located • public: abstract interface to all clients • Writing a subclass often requires: • More access than a generic client • Less access than whole concrete representation • Solution: new visibility level • Keyword: protected • Protected members are inherited but are not part of the public interface to generic clients • Warning: anyone can extend your class and then has access to protected members
Good Practice: Limited Use • Getting it right is hard • Unless you have an explicit need for an open (ie extendable) class hierarchy, prevent others from extending your classes • Keyword final prevents extensions public final class Faculty { . . . } public class Administrator extends Faculty { . . . //compiler complains } • If you do have a specific need to allow extensions, design for it carefully • Use protected diligently and carefully (it’s a huge increase in visibility over private or even over package!) • Chances are, it will still be broken
Summary • Implementation (class) inheritance • Declaration syntax: extends just like interfaces • Vocabulary: super/sub, base/derived, parent/child • Class and interface hierarchies • Constructing new instances • Overriding and polymorphism • Signature must match exactly (use @Override) • Dynamic type controls implementation • Hook methods: dynamic type of this • Protected visibility • Limiting extension: final
Supplemental Reading • Bloch’s “Effective Java”, chapter 3 • See Safari Books Online link • Warning: favors instanceOf over getClass • Better for behavioral subtyping • Worse for creating an equivalence relation • IBM developerWorks paper • “Java Theory and practice: Hashing it out” • http://www.ibm.com/developerworks/java/library/j-jtp05273.html • Various blogs (all slightly broken) • http://www.geocities.com/technofundo/tech/java/equalhash.html