Polymorphism and References
> Click or hit Control-Enter to run Example.main above
Today’s Goal
Connect polymorphism (Monday) and references (Wednesday)
Review: The Tree Of (Java) Life
In Java, each class has a single parent, meaning that classes are organized into a tree.
If we follow each node to its parent, we eventually get to the top, or root…
The Root Object: Object
public class Dog { }
// is equivalent to
public class Dog extends Object { }
If a Java class
does not explicitly extend
another class, it implicitly
extends Object
.
Review: Inherited from Object
public class Dog {
private String name;
Dog(String setName) {
name = setName;
}
}
public class Example {
public static void main(String[] unused) {
Dog chuchu = new Dog("Chuchu");
System.out.println(chuchu.toString());
}
}
All Java objects inherit a small number of important methods from Object
.
As a result, all Java objects implement these methods!
Review: Methods Inherited from Object
For our purposes, the following methods inherited from Object
are important:
-
String toString()
: return aString
representing the instance. Frequently used for debugging. -
boolean equals(Object other)
: return aboolean
indicating whether this object is the same as another object -
int hashCode()
: return anint
uniquely representing an object’s contents. We’ll talk more about hashing later—it’s incredibly important and useful.
Review: Method Overriding
public class Dog {
private String name;
Dog(String setName) {
name = setName;
}
public String toString() {
return name;
}
}
public class Example {
public static void main(String[] unused) {
Dog chuchu = new Dog("Chuchu");
System.out.println(chuchu.toString());
}
}
The default Object
methods are rarely useful.
So classes usually override them and provide their own.
Hierarchical Name and Method Resolution
The Java type hierarchy is used when resolving the names of variables and methods:
-
Does the class have a variable or method with the given name? If so, use it.
-
If not, search the parent class—but limited by
public
andprotected
-
Continue up the tree until the name is found or the search fails
> Click or hit Control-Enter to run Example.main above
Polymorphism
Polymorphism: the provision of a single interface to entities of different types.
We’ll discuss interfaces in more detail next week. For now, let’s identify two kinds of Java polymorphism using examples.
Subtype Polymorphism
public class Pet {
public void printMe() {
System.out.println("I'm a pet");
}
}
public class Dog extends Pet {
public void printMe() {
System.out.println("I'm a dog");
}
}
In Java, every object can be referred to as at least two types:
-
Each
Pet
can also be referred to as anObject
-
Each
Dog
can be referred to as aPet
and also as anObject
Reference Conversion: Upcasting
public class Pet { }
public class Dog extends Pet {
public String toString() {
return "Dog";
}
}
public class Example {
public static void main(String[] unused) {
Dog chuchu = new Dog();
Pet xyz = new Pet();
Example.printAnything(chuchu);
Example.printAnything(xyz);
}
public static void printAnything(Object toPrint) {
System.out.println(toPrint.toString());
}
}
Java will upcast object references automatically.
> Click or hit Control-Enter to run Example.main above
But Instances Retain Their Types
public class Pet { }
public class Dog extends Pet {
public String toString() {
return "Still a Dog";
}
}
public class Example {
public static void main(String[] unused) {
Dog chuchu = new Dog();
Object chuchuAsObject = chuchu;
System.out.println(chuchuAsObject);
Pet chuchuAsPet = chuchu;
System.out.println(chuchuAsPet);
}
}
> Click or hit Control-Enter to run Example.main above
Reference Conversion: Downcasting
public class Pet { }
public class Dog extends Pet {
public String toString() {
return "Still a Dog";
}
}
public class Example {
public static void main(String[] unused) {
Object chuchu = new Dog();
Example.printAnything(chuchu);
Pet chuchuAsPet = (Pet) chuchu; // chuchu is a Pet, so this works
Example.printAnything(chuchuAsPet);
}
}
We can also cast references down but only if the instance is actually the appropriate subtype.
Java checks the cast at runtime to make sure that it is appropriate.
> Click or hit Control-Enter to run Example.main above
Type Testing: instanceof
public class Pet { }
public class Dog extends Pet { }
public class Cat extends Pet { }
public class Example {
public static void main(String[] unused) {
Pet chuchu = new Dog();
Pet xyz = new Cat();
System.out.println(chuchu instanceof Dog); // Prints true
System.out.println(chuchu instanceof Pet); // Prints true
System.out.println(chuchu instanceof Cat); // Prints false
}
}
The Java instanceof
operator allows you to test whether an object is an
instance of or a descendant of a particular class.
> Click or hit Control-Enter to run Example.main above
Review: Reference Variables
class Person { }
/*
* me is declared to hold a reference to an object of type Person,
* but currently refers to nothing.
*/
Person me;
/*
* Initializing an instance to null is another way of indicating
* that it currently refers to nothing.
*/
Person you = null;
me = new Person(); // Now me refers to a new Person object
you = me; // Now me and you refer to the same Person object
System.out.println(you == me); // The variables store the same reference
you = new Person(); // Now you refers to a new Person object
System.out.println(you == me);
We can (and will) refer to a Java variable that refers to an object as a reference variable.
Review: References Are Not Objects
References are not the thing the refer to.
What are some real-world examples of references?
-
A phone number: which refers to a phone
-
A street address: which refers to a physical location
-
A social security number: which refers to a person
Instance v. Reference Types
public class Pet { }
public class Dog extends Pet { }
public class Example {
public static void main(String[] unused) {
Dog chuchu = new Dog(); // Reference type Dog, instance type Dog
Object chuchuAsObject = new Dog(); // Reference type Object, instance type Dog
Pet chuchuAsPet = chuchu; // Reference type Pet upcast from Dog reference
chuchu = (Dog) chuchuAsObject; // Reference type Dog downcast from Object
reference
}
}
-
Instance type: whatever is to the right of
new
, the actual type of the object, never changes -
Reference type: the type of the reference that we use to refer to an object, can be the instance type or any of its ancestors
Reference Types
public class Pet { }
public class Dog extends Pet {
public void woof() {
System.out.println("Bark");
}
}
public class Example {
public static void main(String[] unused) {
Dog chuchu = new Dog();
chuchu.woof(); // I can call woof on a Dog reference
Object chuchuAsObject = chuchu;
chuchuAsObject.woof(); // I can't call woof on an Object reference...
Object stringObject = new String("String");
stringObject.woof(); // ...because not every object implements woof()!
System.out.println(stringObject.toString()); // But I can call toString
}
}
In Java the reference type controls what instance methods and variables we can access.
> Click or hit Control-Enter to run Example.main above
Why Polymorphism?
This really gets to the purpose behind Java’s entire type system.
-
Descendant classes can implement or override ancestor behavior while retaining desirable ancestor properties
-
Polymorphism makes it possible to write methods that work for any descendant class—even ones that you may not have created
Generality v. Capability
Polymorphism presents one of many tradeoffs in computer program and system design:
-
Higher on the object hierarchy: more general, but can use fewer capabilities
-
Lower on the object hierarchy: less general, but can use more capabilities
Generality v. Capability: A Metaphor
You meet someone on a plane.
-
They’re a person, so you can talk about…
-
Then you find out they’re a student, so you can talk about…
-
Then you find out they’re an Illinois student, so you can talk about…
-
Then you find out they’re a CS 125 student, so you can talk about…
> Click or hit Control-Enter to run Example.main above
More Class Design: abstract
Marking a class as abstract
means that it can only be extended and cannot
be instantiated:
public abstract class Pet { }
public class Dog extends Pet { }
Pet pet = new Pet(); // This will not work
Dog dog = new Dog(); // This will work
> Click or hit Control-Enter to run Example.main above
private
Classes?
In Java classes cannot be marked as private
: that would make little sense,
since nobody could use them.
-
To use it you have to create one
-
To create one you have to call one of it’s methods (the constructor)
-
But you can’t call it’s methods because the entire class is
private
Inner Classes
But we can achieve something similar using so-called inner classes:
public class Dog {
class DogFood {
public String toString() {
return "kibble";
}
}
private DogFood myFood;
Dog() {
myFood = new DogFood();
}
}
> Click or hit Control-Enter to run Example.main above
Questions About Polymorphism, References, or Class Design?
Announcements
-
Wednesday 5–8PM office hours have been canceled. We will now hold office hours on Fridays (today) from 5–8PM.
-
We have a anonymous feedback form to the course website. Use it to give us feedback!