Chapter 9: Polymorphism Coming up: Creating Objects Revisited Today’s lecture • Review polymorphism • examples Polymorphism • Polymorphism is an core object-oriented concept that allows us to create a variable with "more than one form" • We've seen this throughout the semester: with inheritance, interfaces • If an object is of type Student, where Student extends Person, we could say Object thing = getStudent(); Person person = getStudent(); Student jane = getStudent(); Design in Java • Consider object.toString() • The question is, what does the toString method actually do? • That is, what does the code for toString look like when it is called on some object? • The answer is, "it depends" • It depends on what is the actual type of the object, in memory • This may or may not be the type that the reference to the object was given toString and polymorphism • import java.util.*; public class Test{ public static void main(String[] args){ ArrayList list = new ArrayList(); list.add(new Object()); list.add(new String("Kinga")); list.add(new Integer(3)); list.add(new ArrayList()); for(Object o : list){ System.out.println(o.toString()); System.out.println(o.getClass()); } } } Will this work? • import java.util.*; public class Test{ public static void main(String[] args){ ArrayList list = new ArrayList(); list.add(new Object()); list.add(new String("Kinga")); list.add(new Integer(3)); list.add(new ArrayList()); for(int i = 0; i < list.size(); i++){ String o = ((String)list.get(i)); System.out.println(o); } } } Binding • Consider the following method invocation: obj.toString(); • At some point, this invocation is bound to the definition of the method that it invokes • i.e., we figure out which toString we actually run • If this binding occurred at compile time, then that line of code would call the same method every time • However, Java defers method binding until run time -this is called dynamic binding or late binding Polymorphism •The term polymorphism literally means "having many forms" •A polymorphic reference is a variable that can refer to different types of objects at different points in time •The method invoked through a polymorphic reference can change from one invocation to the next •All object references in Java are potentially polymorphic References and Inheritance •Assigning a child object to a parent reference is considered to be a widening conversion, and can be performed by simple assignment •Assigning a parent object to a child reference can be done also, but it is considered a narrowing conversion and must be done with a cast •The widening conversion is the most useful Which of the following are valid in Java? • Person jane = new Person(); • Person jane = new Object(); • Person jane = new Person(); jane = new Student(); //child class* • Student eric = new Student(); Person jane = eric; • Student eric = new Student(); Person jane = (Person)eric; • Student eric = new Person(); • Person jane = new Person(); Student eric = jane; • Person jane = new Person(); Student eric = (Student)jane; * Student is a child class of Person Solution •Java only honors an assignment where the contract between interfaces is honored •Pick the most surprising/confusing example from the previous slide and see how the interface was/was not honored → or try it out in Eclipse So, does Java even care about types? •It seems like we can assign an object to a reference that is not the same type as the object itself •Yes, we can! As long as it makes sense to…i.e., the contract is honored •Java is smart •It knows the actual type of the object in memory •It allows you to be flexible and use any compatible types Flexibility • Can you think of an example where this might be useful? (How have we used it, already?) • Imagine we have a collection of animals of different types… • Lets us write specialized code for every animal, rather than writing messy code in one animal class that is supposed to handle all the different types of animals • How does Java know what kind of animal it is when the speak method is called? Polymorphism and Inheritance • Widening conversion: • Object object = new String("kinga"); • Narrowing conversion (will compile): • Person jane = new Person(); Student eric = (Student) jane; • Illegal narrowing conversion: • Person jane = new Person(); Student eric = jane; • Why? Polymorphism via Interfaces • Suppose two classes, Philosopher and Dog, both implement the Speaker interface, providing distinct versions of the speak method • In the following code, the first call to speak invokes one version and the second invokes another: Speaker guest = new Philospher(); guest.speak(); guest = new Dog(); guest.speak(); Question •What is the difference between interfaces and inheritance? How does Java know the type of an object at runtime? • This information is stored in memory, along with the contents of the object • Why can't this sort of thing be decided at compile time? • A compiler can only check for certain things, and implementing polymorphism correctly isn't one of them Imagine a collection of Animals • ArrayList animals = DB.getAnimals(); • animals contains Elephants, Lions, and Bears for(int i = 0; i < animals.size(); i++) ((Animal)animals.get(i)).speak(); • Will call the specialized speak method • Collections are a natural way to use polymorphism in Java • What else could we want to do this array? Why are collections useful? Collection interfaces •ArrayList implements the Collection interface •Also implements List, which extends Collection •Why use a Collection? •Collections.sort(List) can sort an ArrayList according to the natural ordering of its elements •Elements have a natural ordering if they’re Comparable Why do we care about sorting? we're dealing with information • Need some way to search it, • Therefore need some way to index it, • Therefore need some way to order the items (unless we want terribly long searches) • Comparing and searching are at the heart of many applications • Maintaining a sorted list helps these operations go faster