4_ccps209_inheritance_polymorphism.pdf

Full Transcript

C/CPS 209 Computer Science II Prof. Alex Ufkes Topic 4: Inheritance and Polymorphism Notice! Obligatory copyright notice in the age of digital...

C/CPS 209 Computer Science II Prof. Alex Ufkes Topic 4: Inheritance and Polymorphism Notice! Obligatory copyright notice in the age of digital delivery and online classrooms: The copyright to this original work is held by Alex Ufkes. Students registered in course C/CPS 209 can use this material for the purposes of this course, but no other use is permitted, and there can be no sale or transfer or use of the work for any other purpose without explicit permission of Alex Ufkes. © Alex Ufkes, 2020, 2024 2 Course Administration Keep up with the labs! The term is condensed, the course moves very fast. © Alex Ufkes, 2020, 2024 3 Any Questions? © Alex Ufkes, 2020, 2024 4 Let’s Get Started! © Alex Ufkes, 2020, 2024 5 Previously Static members in Math and Arrays cos(), sin(), sqrt() are all static methods. We don’t have to create a Math object to use them © Alex Ufkes, 2020, 2024 6 Previously Static members in Math and Arrays Look familiar? Like Math, methods in the Arrays class are all static. String methods belong to String objects Array methods belong to the Arrays class © Alex Ufkes, 2020, 2024 7 Previously Follows typical object declaration syntax. Add as many elements as we want. Items added to end of list, ArrayList grows as needed. Very powerful, the programmer is © Alex Ufkes, 2020, 2024 not responsible for basic behavior. 8 Previously Auto-boxing Auto-unbox to perform addition Auto-box result of addition to store back in object d1. Wrapper objects can be used like their wrapped primitives They also provide advantages due to being objects, such as being members of ArrayLists © Alex Ufkes, 2020, 2024 9 Previously BigInteger grows arbitrarily large like a Python integer: (import java.math.*; to use) © Alex Ufkes, 2020, 2024 10 Today: Inheritance and Polymorphism Why inheritance? Inheritance using extends, all the various rules and nuances. Polymorphic methods © Alex Ufkes, 2020, 2024 11 Inheritance Motivation Classes provide a mechanism for extending the primitive type system in Java There is no such “Account” type in Java, so we create an Account Class © Alex Ufkes, 2020, 2024 12 Inheritance Motivation This Account class is too simple: There are many kinds of bank account. Savings, chequing, investment, RRSP, etc. Each might require different methods and instance variables. © Alex Ufkes, 2020, 2024 13 Option 1: Additional Properties Shared by all account types Savings, RRSP, investment Savings, investment, chequing Savings RRSP GIC © Alex Ufkes, 2020, 2024 14 Option 1: Additional Properties This is poor practice. Declaring a chequing account means we get many unrelated properties. © Alex Ufkes, 2020, 2024 15 Option 1: Additional Properties This is bad class design. But why? Recall: Classes are used to model specific concepts within a problem domain. Properties or behaviors falling outside that domain should not be included. Absurd example: BankAccount should not have a turnLeft() method. More subtle: a chequing account should not have a minimumTerm property. © Alex Ufkes, 2020, 2024 16 Option 1: Additional Properties Practically Speaking: Code is messier. Wastes memory Poor encapsulation © Alex Ufkes, 2020, 2024 17 Thoughts? © Alex Ufkes, 2020, 2024 18 Classes are used to model specific concepts within a problem domain. What if we considered different accounts to be different problem domains? © Alex Ufkes, 2020, 2024 19 Option 2: Additional Classes We could create separate classes for each account type: Better! Is there still a problem with this? © Alex Ufkes, 2020, 2024 20 Option 2: Additional Classes Problem with this? Lots of duplicate code! Instance variables and methods are duplicated. Changing a method can mean changing it in multiple places. © Alex Ufkes, 2020, 2024 21 Lots of duplicate code! Instance variables and methods are duplicated. Changing a method can mean changing it in multiple places. Consider: o We want deposit() to be the same for all account types o We want to change the way deposit() works o We will have to change deposit() for every single account class © Alex Ufkes, 2020, 2024 22 Consider: o We want deposit() to be the same for all account types o We want to change the way deposit() works o We will have to change deposit() for every single account class There might be dozens of different Account classes At best, it’s tedious to go through all of them individually. Worse, we might miss one, and it will no longer work as intended. © Alex Ufkes, 2020, 2024 23 Option 2: Additional Classes Despite duplicate code, we’ve addressed several issues: Objects in memory are only as big as they need to be. No danger in accidentally using a chequing account as a savings account, etc. Wouldn’t it be nice if classes could somehow share code? © Alex Ufkes, 2020, 2024 24 Inheritance © Alex Ufkes, 2020, 2024 25 Inheritance What is inheritance? One class can have its definition built upon another class. Sub-class built on super-class, child class built on parent class, etc. What gets inherited from parent by child varies by language. In Java, sub-class inherits public variables and methods. Child is free to add additional variables and methods. A Class should model properties and behaviors specific to itself, and common to all its subclasses © Alex Ufkes, 2020, 2024 26 Vertebrate Mammal Fish Bird Lion Bear Trout Blue Jay Sparrow Polar Grizzly This is a categorization hierarchy that we’re all familiar with. Classes can (and should) be modelled in much the same way. Known as an inheritance hierarchy © Alex Ufkes, 2020, 2024 27 Vertebrate Inheritance models the kind-of relationship: Mammal Grizzly is a kind-of Bear, Bear is a kind-of mammal, and Mammal is a kind-of Vertebrate. is a kind-of Lion Bear o But not vice-versa! Inheritance only goes one way: Grizzly is a kind-of Bear, but Bear is not a kind-of Grizzly. Polar Grizzly How might our BankAccount class hierarchy look? © Alex Ufkes, 2020, 2024 28 BankAccount SavingsAccount ChequingAccount RRSP TFSA GIC There is no “right way” to arrange this. The optimal way may not be obvious. Should we have separate classes for USD and CAD accounts? InvestmentAccount Should we instead use an instance variable to indicate currency? USD CAD © Alex Ufkes, 2020, 2024 29 Java Class Hierarchy Every single Class in Java is a descendant of class Object BankAccount This relationship occurs by default, unless we explicitly indicate a different superclass. © Alex Ufkes, 2020, 2024 30 Capital-O Object Don’t confuse the Object class with the broader concept of an object. Object is the top-level class in Java from which all others inherit, be it directly or through some longer lineage. We know that a subclass inherits public instance variables and methods from its superclass. Every class we’ve created has been (by default) a subclass of Object. Have we been unknowingly inheriting anything useful from Object? © Alex Ufkes, 2020, 2024 31 Example: toString() When a primitive argument is passed to System.out.println(), its value is printed. What happens if we pass an object reference? How should a BankAccount be printed? Not so obvious! When we pass ANY object reference to println(), its toString() method is invoked internally. If you haven’t implemented the toString() method, the version inherited from Object is invoked instead. © Alex Ufkes, 2020, 2024 32 Example: toString() Empty! Doesn’t matter! Name of the class Hash code of object in hexadecimal © Alex Ufkes, 2020, 2024 33 Quick Aside: hashCode() A hash code is a numeric representation of the contents of a given object. Much bigger topic in CPS305. We also inherit the hashCode() method from Object. © Alex Ufkes, 2020, 2024 34 Override toString() main() can stay the same: toString() is just like any other method. No arguments, returns a string. Return value is printed by println() © Alex Ufkes, 2020, 2024 35 Override toString() main() can stay the same: © Alex Ufkes, 2020, 2024 36 Override toString() BankAccount overrides toString() If we call a method that doesn’t exist in the subclass, Java checks the superclass. Continue up to Object until found. If not found at all, compile error. Though not required, it is good practice to indicate, using @Override, when a method is overriding an existing method. © Alex Ufkes, 2020, 2024 37 Inheritance with extends Every bank account will have a balance. Let’s put it in the generic BankAccount class RRSP will have its own deposit method It doesn’t need its own variable for balance There’s a problem! We haven’t actually specified inheritance yet. © Alex Ufkes, 2020, 2024 38 Inheritance with extends Once we add this syntax, RRSP can access public methods and instance variables of BankAccount © Alex Ufkes, 2020, 2024 39 Before we move on: Just your daily reminder that this is bad practice. Instance variables should never be public. Use public accessor methods instead (gets) I’m showing it this way to simplify the code in the slides. © Alex Ufkes, 2020, 2024 40 © Alex Ufkes, 2020, 2024 41 © Alex Ufkes, 2020, 2024 42 Inheritance: Multiple extends? Nope. Java supports single inheritance only. There are mechanisms for achieving something resembling multiple inheritance. We’ll see that later. A class can only have a single superclass. However, a class may have any number of subclasses. We can have any number of account classes all extending BankAccount. © Alex Ufkes, 2020, 2024 43 Inheritance: Code Reuse This code is the same! What can we do? © Alex Ufkes, 2020, 2024 44 Inheritance: super Use super to access a superclass method. super is a reference to the superclass object Avoid duplicating code in an overridden method! © Alex Ufkes, 2020, 2024 45 Useful Example: super.toString() © Alex Ufkes, 2020, 2024 46 Useful Example: super.toString() © Alex Ufkes, 2020, 2024 47 super VS this Recall: this lets us differentiate between local variables and instance variables. © Alex Ufkes, 2020, 2024 48 super VS this Recall: super accesses the instance variable (or method) of the superclass © Alex Ufkes, 2020, 2024 49 final Classes Final classes cannot be extended. © Alex Ufkes, 2020, 2024 50 final Classes Final classes cannot be extended. Use final if you don’t want other programmers to be able to extend your class. This class is the way it is, we don’t want to allow any functionality to be built on top of it. We can also have final methods. A final method cannot be overridden. © Alex Ufkes, 2020, 2024 51 final Classes String is a final class © Alex Ufkes, 2020, 2024 52 Inheritance Rules Private methods and instance variables are not inherited. © Alex Ufkes, 2020, 2024 53 Inheritance Rules We get around this by using good Java practices and implementing public set and get methods instead. © Alex Ufkes, 2020, 2024 54 Inheritance Rules Private methods and instance variables are not inherited. Java uses single inheritance. A class can only have one superclass © Alex Ufkes, 2020, 2024 55 Inheritance Rules Private methods and instance variables are not inherited. Java uses single inheritance. A class can only have one superclass Inheritance cannot be circular © Alex Ufkes, 2020, 2024 56 Inheritance & Constructors Recall: If we don’t create one, Java synthesizes a default, empty constructor. Defining our own constructor means that Java no longer creates an empty one for us. Now, we can’t create a BankAccount without supplying an argument to the constructor What happens to RRSP? © Alex Ufkes, 2020, 2024 57 Inheritance & Constructors Recall: If we don’t create one, Java synthesizes a default, empty constructor. Every class MUST have a constructor. Constructors are NOT inherited. Every class, regardless of inheritance, will have a constructor created for it if we don’t explicitly define one. © Alex Ufkes, 2020, 2024 58 Inheritance & Constructors RRSP doesn’t have a constructor, so Java creates one. However, this doesn’t match BankAccount, so there’s an error. What does this tell us? © Alex Ufkes, 2020, 2024 59 Inheritance & Constructors When we instantiate an object whose class extends another class, the constructor of the superclass is called implicitly. This is what Java creates for us when we do not create our own constructor. super() invokes the superclass constructor In this case, super() is invoking the empty constructor of Object. © Alex Ufkes, 2020, 2024 60 Inheritance & Constructors Even if we create our own constructor, Java implicitly invokes the superclass constructor We need not include this statement, though we can if we want. © Alex Ufkes, 2020, 2024 61 Inheritance & Constructors Back to RRSP: This is what Java creates What’s wrong? BankAccount no longer has an empty constructor! © Alex Ufkes, 2020, 2024 62 Inheritance & Constructors Two ways to fix, depending on what you’re going for: 1. Add empty constructor: Now, super() in RRSP invokes empty constructor in BankAccount © Alex Ufkes, 2020, 2024 63 Inheritance & Constructors Two ways to fix, depending on what you’re going for: 2. Add appropriate constructor to RRSP: These now match! © Alex Ufkes, 2020, 2024 64 Inheritance & Constructors How is this useful in practice? Must come first! RRSP constructor initializes instance variables unique to RRSP © Alex Ufkes, 2020, 2024 65 Inheritance & Constructors: Summary Java creates an empty constructor with a call to the superclass constructor implicitly Even if we create our own constructor, super() is added implicitly as the first statement in the constructor Subclass constructors must adhere to the signature of the superclass constructor Call to superclass constructor must come first © Alex Ufkes, 2020, 2024 66 Call Order Constructors are executed down the line, starting at Object: To instantiate an RRSP object, we first instantiate a BankAccount object. To instantiate a BankAccount object, we first instantiate an Object object. Object’s constructor returns to BankAccount, BankAccount’s constructor returns to RRSP. © Alex Ufkes, 2020, 2024 67 But Why? A subclass has access to methods and instance variables of the superclass. Thus, we can’t have a subclass object without also having the methods and variables of its superclass. In practice, Java doesn’t actually create multiple objects in memory. We get a single object in memory containing all variables and methods within the inheritance chain. © Alex Ufkes, 2020, 2024 68 © Alex Ufkes, 2020, 2024 69 Polymorphism We get a single object on the heap containing all variables and methods within the inheritance chain. If BankAccount has public method deposit(), every subclass of BankAccount is guaranteed to have method deposit(). This allows us to write parameter polymorphic methods. That is, a method whose parameter type is some superclass. This method would be able to accept any class that inherits from the superclass. Talk is cheap. Let’s see an example. © Alex Ufkes, 2020, 2024 70 © Alex Ufkes, 2020, 2024 71 What’s going on here? BankAccount defines a deposit() method. Thus, subclasses of BankAccount do also. Could be overridden, could be inherited. However! Our atm() method can only invoke methods that exist in BankAccount. RRSP might define a new method not present in BankAccount. This could not be invoked from atm() This gets confusing. Always remember the kind-of relationship! An RRSP is a kind of BankAccount, so any method designed for a BankAccount will/should also work on an RRSP. A BankAccount is not a kind of RRSP. A method whose parameter is an RRSP will NOT accept a BankAccount. © Alex Ufkes, 2020, 2024 72 Upcasting Who remembers casting? It works with classes too! BankAccount b1 RRSP double balance void deposit() RRSP acc Etc… This is an implicit cast. We could also write: BankAccount b1 = (BankAccount) acc; © Alex Ufkes, 2020, 2024 73 Upcasting Upcasting: A sub-class object can be treated as an instance of its super-class Why does this work? BankAccount b1 An RRSP can do everything a BankAccount can do If a BankAccount reference points to an RRSP object, there’s no danger RRSP double balance Liskov Substitution Principle: void deposit() “Functions that use pointers or references RRSP acc Etc… to base classes must be able to use objects of derived classes without knowing it” © Alex Ufkes, 2020, 2024 74 Downcasting? BankAccount b1 RRSP double balance void deposit() RRSP acc Etc… We do have to explicitly cast here. We know that b1 references an RRSP object, but the compiler doesn’t. RRSP acc2 © Alex Ufkes, 2020, 2024 75 Downcasting? BankAccount b BankAccount double balance void deposit() RRSP r Etc… Problem….? © Alex Ufkes, 2020, 2024 76 Downcasting? Java accepts that we’ve cast reference b as an RRSP. It’s not until runtime that Java follows the reference to the object and detects the problem. No other choice: the object doesn’t actually exist at compile time. © Alex Ufkes, 2020, 2024 77 Useful Tools: instanceof Can be used to determine if an object is an instance of a particular class © Alex Ufkes, 2020, 2024 78 Useful Tools: getClass() Use to check if an object is a specific instance of a class. Not just a subclass. © Alex Ufkes, 2020, 2024 79 Object Recall: Every class is a descendant of Object Object is not some abstract ephemeral notion from the ether. We can instantiate an Object object if we want. It is often very useful to consider objects as instances of their most primitive ancestor, Object. Object defines many useful methods that are inherited by all Java classes. We’ve already seen toString(), how about another? © Alex Ufkes, 2020, 2024 80 Object.equals() Check if two variables reference the same object This code does not copy the object in memory. It copies the reference a a Bird Changes to a and b affect the same object. b © Alex Ufkes, 2020, 2024 81 Object.equals() Check if two variables reference the same object Notice this mirrors == between objects. == cannot be overridden. Object.equals() can. © Alex Ufkes, 2020, 2024 82 Object.equals() Use new to get a new object Bird a Bird b © Alex Ufkes, 2020, 2024 83 Object.equals() Use new to get a new object © Alex Ufkes, 2020, 2024 84 Overriding equals() It is very often desirable to compare two objects based on some class-specific criteria. It’s nice to know if two references refer to the same object, but we can just as easily use == for that. Consider… © Alex Ufkes, 2020, 2024 85 Point: Overriding equals() If we define a class called Point: We might define two Points as being equal if their x and y values are the same To implement this in Java, we override the equals() method To do so, we apply almost all the principles we learned today. © Alex Ufkes, 2020, 2024 86 Point: Overriding equals() equals() accepts an Object as an argument Check if argument is the exact same object If they are, then their x and y values are certainly the same. Ensure input argument is in fact a Point. If not, comparing their x and y fields is unlikely to work. If they’re two different Points, compare x and y, return result (true/false) © Alex Ufkes, 2020, 2024 87 Point: Overriding equals() Parameter polymorphic method. Object is a superclass of Point. Downcasting. We verified that o is a Point. We can safely cast it as such. © Alex Ufkes, 2020, 2024 88 © Alex Ufkes, 2020, 2024 89 Point int x, y this boolean equals (Object o) Different objects! Point int x, y boolean equals Object o (Object o) © Alex Ufkes, 2020, 2024 90 Point int x, y this boolean equals (Object o) Point int x, y boolean equals Reference o does refer to Object o (Object o) a Point object! © Alex Ufkes, 2020, 2024 91 Point int x, y this boolean equals (Object o) Point p Point int x, y boolean equals Object o (Object o) © Alex Ufkes, 2020, 2024 92 Overriding -VS- Overloading Very Important! This is OVERLOADING: The equals method in Object does not accept a Point as an argument, but rather an Object. Invoking this with a non-Point argument will actually be invoking Object’s version. Remember Object’s version simply checks if they’re the same object. © Alex Ufkes, 2020, 2024 93 Overriding -VS- Overloading Very Important! Object Point boolean equals int x, y (Object o) boolean equals (Point o) Invoking this with a non-Point argument will actually be invoking Object’s version. © Alex Ufkes, 2020, 2024 94 Overriding -VS- Overloading Very Important! This is OVERLOADING The equals method in Object does not accept a Point as an argument, but rather an Object. This is OVERRIDING This method signature is identical to its counterpart in Object. © Alex Ufkes, 2020, 2024 95 Overriding -VS- Overloading @Override This is an excellent reason to always include @Override, if that is our intention. By doing so, the compiler will complain if we’re accidentally overloading. © Alex Ufkes, 2020, 2024 96 Overriding -VS- Overloading @Override This is an excellent reason to always include @Override, if that is our intention. By doing so, the compiler will complain if we’re accidentally overloading. © Alex Ufkes, 2020, 2024 97 Overriding equals() Some general best practices: When overriding equals(), ensure the following hold true: If a.equals(b), then b.equals(a) should be true. If a.equals(b), and b.equals(c), then a.equals(c) should be true. A meaningful equals method is critical! Other classes rely on equals(). Specifically, methods add() and contains() from the Collection class (later). © Alex Ufkes, 2020, 2024 98 Summary: Inheritance and Polymorphism Why inheritance? Inheritance using extends, all the various rules and nuances. Polymorphic methods Overriding equals() © Alex Ufkes, 2020, 2024 99 © Alex Ufkes, 2020, 2024 100

Use Quizgecko on...
Browser
Browser