KotlinCS 124 LogoJava

Polymorphism

public class Pet {
public void speak() {
System.out.println("I'm a pet!");
}
}
public class Dog extends Pet {
public void woof() {
System.out.println("I'm a dog!");
}
}
public class Cat extends Pet {
public void meow() {
System.out.println("I'm a cat!");
}
}
public void speak(Pet pet) {
if (pet instanceof Dog dog) {
dog.woof();
} else if (pet instanceof Cat cat) {
cat.meow();
} else {
pet.speak();
}
}

Next we’ll continue to practice with inheritance. We’ll also introduce a new big (literally) idea—polymorphism. Polymorphism may sound scary, but it’s not, and we’ll work it out together like we always do, using a lot of examples.

Another Puzzle
Another Puzzle

Let’s look at another example of puzzling Java code together:

void printIt(Object it) {
System.out.println("OK, I'll print it! " + it);
}
public class Pet { }
public class Course { }
String string = "A String";
Pet pet = new Pet();
Course course = new Course();
printIt(string);
printIt(pet);
printIt(course);

Polymorphism
Polymorphism

Polymorphism is a big word, and sounds a bit scary. But it’s actually quite straightforward. Let’s work it out together starting with the Wikipedia definition:

In programming languages and type theory, polymorphism is the provision of a single interface to entities of different types

“Is A”
“Is A”

One way to think about polymorphism and Java inheritance is to consider “is a” relationships. For example, every instance of any Java class “is a” Object, because every class is a subclass of Object. Other “is a” relationships depend on inheritance relationships established with extends:

// Pet is a Object
class Pet { }
// Dog is a Pet
// Dog is a Object
class Dog extends Pet { }
// Mutt is a Dog
// Mutt is a Pet
// Mutt is a Object
class Mutt extends Dog { }

Method Overriding and Polymorphism
Method Overriding and Polymorphism

One frequent confusion regarding polymorphism has to do with overriding inherited methods. Let’s look at how that works:

class Pet {
public String getType() {
return "Pet";
}
}
class Dog extends Pet {
public String getType() {
return "Dog";
}
}
class Cat extends Pet { }
void greetPet(Pet pet) {
System.out.println("Hello " + pet.getType());
}
Dog dog = new Dog();
Cat cat = new Cat();
greetPet(dog);
greetPet(cat);

Up and Down Casting
Up and Down Casting

When we create an instance of a class, we can save it into a variable of any type that it can morph into:

class Pet { }
class Dog extends Pet { }
Dog dog = new Dog();
Pet pet = new Dog();
Object o = new Dog();

This is referred to as upcasting. Java will automatically upcast an instance to any of its supertypes. Because Dog extends Pet and Pet extends Object, a Dog can be stored in a Dog, Pet, or Object variable.

However! The type of the variable determines what we can do with that object. Let’s look at how.

class Pet { }
class Dog extends Pet { }
Dog dog = new Dog();
Pet pet = new Dog();
Object o = new Dog();

Don’t worry if this seems a bit fuzzy now. We’ll return to this topic a few lessons from now when we discuss object references.

Down Casting and instanceof
Down Casting and instanceof

Consider the type hierarchy established below: Given a Pet variable, it might refer to a Dog, a Cat, a Pet or some other kind of pet!

class Pet { }
class Dog extends Pet {
void bark() { }
}
class Cat extends Pet {
void meow() { }
}
Pet dog = new Dog();
Pet cat = new Cat();
Pet pet = new Pet();

Is there a way that we can tell? Yup! To test if an object is an instance of a particular class, we use the instanceof operator. And, once we determine that cat is actually an instance of Cat, there is another step that we have to take before we can call meow. Let’s examine how that works:

class Pet { }
class Dog extends Pet {
void bark() { }
}
class Cat extends Pet {
void meow() { }
}
Pet dog = new Dog();
Pet cat = new Cat();
Pet pet = new Pet();

Practice: Solve Polymorphic Greeter

Created By: Geoffrey Challen
/ Version: 2020.9.0

Create a public class named Greeter that provides a single class method named greet. greet should accept a single parameter, a Person to greet, and return a String, the greeting. Depending on what kind of person it is, you should greet them differently:

  • If the person is a Professor—for instance, one named "Geoff"—you should greet them "Hi Professor Geoff"
  • If the person is a Student—for instance, one named "Friendly"—you should greet them "Hey Friendly, you are not alone!"
  • If the person is a Staff, then they will have a String role you can retrieve using getRole. For example, if their role is "advising" their name is "Chuchu", you should greet them "Thanks Chuchu for all your help with advising".

All Persons have a name that you can retrieve using getName. If the person is null or not one of the kinds of people described above, return null. Do not solve this problem using method overloading. And do not hard-code the answers. Your solution should work for any Professor, Student, or Staff.

Steady There
Steady There

The last two lessons have been pretty loaded with new ideas and concepts! Exciting, but also enough to make your head spin.

Don’t worry. Over the next two lessons we’ll slow down and review what we’ve learned. And then, over the lessons that follow we’ll have even more opportunities to integrate this knowledge, but with a small twist. So be patient. This won’t all make sense immediately. But it will all make sense eventually.

Practice: Solve Polymorphic Orderer

Created By: Geoffrey Challen
/ Version: 2021.9.0

Create a public class named Orderer that provides a single class method named order. order should accept a single parameter, a Restaurant to order from, and return a String, a comment on your order. Depending on which subclass of Restaurant it is, you should respond differently:

  • If the restaurant is a Fancy restaurant—for instance, with name "MIGA"—you should order "At MIGA I'll order something inexpensive"
  • If the restaurant is a FastFood restaurant, for instance, with with name "Chipotle"—you should order "At Chipotle I'll order something healthy"
  • If the restaurant is a Vegan restaurant, then it will have a String property cuisine you can retrieve using getCuisine. For example, if it its cuisine is "Thai" and name is "Vegan Delight", you should order "At Vegan Delight I'll order delicious Thai food".

All Restaurants have a name that you can retrieve using getName. If the restaurant is null or not one of the kinds described above, return null. Do not solve this problem using method overloading. And do not hard-code the answers. Your solution should work for any Fancy, FastFood, or Vegan instance.

Note that we are not implying that there are not fancy vegan restaurants or fancy fast food restaurants or vegan fast-food restaurants. If anything, the ability of real entities to resist strict classification is one of the limitations of Java's object model.

Homework: Solve Polymorphic Visitor

Created By: Geoffrey Challen
/ Version: 2022.9.0

Create a public class named Visitor that provides a single class method named visit. visit should accept a single parameter, a Place to visit, and return a String, a remark on how to visit that place. Depending on which subclass of Place it is, you should respond differently. Every place has a name you can retrieve using getName. assert that the passed place is notnull`.

  • If the place is a Building—for instance, one named "Siebel"—you should respond "Let's go to the Siebel building."
  • If the place is a Park—for instance, one named "West Side"—you should respond "Let's visit West Side park."
  • If the place is a Restaurant, then it will have a String property cuisine you can retrieve using getCuisine. For example, if the cuisine is "thai" and the name is "Thara Thai", you should respond "Let's try thai food at Thara Thai."

If the place is not one of the kinds described above, return "Whoa, mysterious!". Do not solve this problem using method overloading! And do not hard-code the answers. Your solution should work for any Building, Park, or Restaurant.

Note that many restaurants are also buildings. The tendency of real entities to resist strict classification is one of the limitations of Java's object model.

More Practice

Need more practice? Head over to the practice page.