// APS101, Winter 2009: Lecture 38 (Apr. 13) // // Review: last time we began learning about the details of Object-Oriented concepts, such as Inheritance, method overloading, method overriding, dynamic method binding, and polymorphism. // Today we'll continue learning about all of those concepts, using the Student, StudentAthlete, and StudentNerd classes. // method overloading: same method name & return type, but different arguments. // objectName.methodName() // objectName.methodName(String, int) // objectName.methodName(int, String) // method overriding: exact same method signature, but in a different class (a subclass). // objectName.methodName(args) // anotherObjectName.methodName(args) // these are 2 examples of Polymorphism. Student s = new Student("John", 8923423); s.toString() StudentAthlete s1 = new StudentAthlete("Bob", 98023423, "Golf") s1.toString() s1.getSport() StudentNerd s2 = new StudentNerd("Sam", 98023423, 1000000); s2.toString() s2.getSport() // error! s2 is a StudentNerd, so it has no knowlege of the StudentAthlete class s2.readBook() s2.toString() s1.readBook() // error! s1 is a StudentAthlete, so it has no knowlege of the StudentNerd class s.readBook() // what's the problem? s.getSport() // what's the problem? // here is the class hierarchy: Object | Student | / \ StudentAthlete StudentNerd Student s3; s3 = s1; // remember s1 (Athlete) is still a Student s3.toString() // this is an example of "dynamic method binding": technically there is no Student object, so Java searches Student's subclasses for an alternative at run-time and discovers that s1 also has a toString() method - so it calls it. s3.getSport() // doesn't work! why? s3 = s2; // remember s2 (Nerd) is still a Student s3.toString() s3.readBook() // doesn't work! why? StudentNerd s4; s4 = s1; // s1 is an Athlete, not a Nerd, so cannot assign! StudentNerd s5; s5 = s2; // s2 is a Nerd, not an Athlete, so cannot assign! // How do we know what object s refers to? We use the "instanceof" operator: s instanceof Object s instanceof Student s instanceof StudentAthlete // false s instanceof StudentNerd // false s1 instanceof Object s1 instanceof Student s1 instanceof StudentAthlete s1 instanceof StudentNerd // error! s2 instanceof Object s2 instanceof Student s2 instanceof StudentNerd s2 instanceof StudentAthlete // error! null instanceof Object // false null instanceof Student // false // syntax of instanceof: // o instanceof O // - checks if o is of type O (returns true if yes) // - also checks if o and O have a parent/child relationship // - returns true: if o is a child/subclass of O // - returns false: if o is a parent/superclass of O Student s = s2; // recall that s2 is an Athlete. // "apparent" type of s is Student // "real" type of s is StudentAthlete // you can cast it back to StudentAthlete to access the extra methods: StudentAthlete s6 = (StudentAthlete) s; s6.getSport() // OR ((StudentAthlete) s).getSport() Student s = new Student("Mike", 8783274); StudentAthlete s4 = (StudentAthlete) s; // doesn't work! why? // In general terms: // this works: Student s = new StudentAthlete/Nerd(...) // We can cast a StudentAthlete/Nerd to a more general type Student. // We just ignore the additional data/methods that StudentAthlete/Nerd has. // this doesn't work: StudentAthlete/Nerd s = new Student(...) // We can't cast a Student to a more specific type StudentAthlete/Nerd. // There are more methods/data in StudentAthlete/Nerd than there are in Student. // also, we can't cast a StudentNerd into StudentAthlete (and vice versa)! // now, let's get some practice with arrays of objects... // (see OO.java for examples of what compiles and what doesn't)