9️⃣

12.9 Polymorphism

Earlier we talked about how inheritance describes an is-a relationship. For example, a Dog is an Animal. A Cat is an Animal, and so is a Chicken. What if we want to do something like store a bunch of Animal objects in an array and call methods specific to a subclass? For example, the Dog objects might all have a bark() method, but Chickens don’t.

That’s where polymorphism comes in. Polymorphism is a fancy word for saying that instances of subclasses can also be considered instances of their superclass. In code, that might look like this:

Animal MyDog = new Dog();

Notice that this time, instead of declaring myDog to be of type Dog, we’re declaring it of type Animal.

We can also do this with things like arrays:

Animal[] animals = {
		new Dog(),
		new Cat(),
		new Chicken(),
};

Here, I created an array of Dog, Cat, and Chicken objects. This is possible because all of those classes inherit from Animal, so polymorphism applies.

There’s even a special keyword called instanceof which lets you check if an object is an instance of a class. For example, the following code would print 1 on one line and 2 on the next line.

if (myDog instance of Dog) {
	System.out.println(1);
}

if (myDog instance of Animal) {
	System.out.println(2);
}

If myDog was not an instance of Dog or Animal, there are a couple things that could happen. If the 2 things on either side of the instanceof operator are related in some way (through inheritance or implementing*) the expression will evaluate to false. (It will also evaluate to false if the thing on the left is null.) If the 2 things are completely not related, there will be a compile error.

⚠️

A class can implement something called an interface, which is similar to inheritance but not quite. We will talk more about interfaces in Ch. 15.

Practice

TestShape

Create 4 classes: TestShape, Shape, Circle, and Rectangle.

Circle and Rectangle extend Shape. TestShape is the driver class.

UML diagrams for Circle, Rectangle, and Shape:

Note: The x and y fields in the Shape class are the coordinates of the center of the shape. The getDistance() method returns the distance between this Shape and another Shape s.

In the TestShape class, create an array of Shape objects. Initialize the array with at least 2 Rectangle objects and 2 Circle objects. Give them any length/width/radius you want. Using a for each loop, check if the current Shape is a Rectangle or a Circle. If it is a Rectangle, print the area and perimeter. If it is a Circle, print the area and circumference.

Feel free to call other methods on the objects to see if they work properly! Also, bonus points for Javadoc comments for all of the methods.

equals()

At the beginning of the course, we learned that to test if two things are equal, we use the equality operator (==). However, you may have noticed that if you use the equality operator to see if 2 objects are equal, it might not give you the result you expected. For example, line 3 of the code below prints false while line 4 prints true.

String word1 = new String("hello");
String word2 = new String("hello");
System.out.println(word1 == word2); // false 
System.out.println(word1.equals(word2)); // true

This is because what the equality operator actually does is that it checks if the two things on either side of it reference the same location in the computer’s memory, not if the contents of the two objects are the same.

If you want to check if the contents of 2 objects are the same, you must use the equals() method, as demonstrated in line 4 of the code above.

Usually, when you design a class, you should also design it so that it overrides the equals() method that is inherited from the Object class (which all classes inherit from by default). The method signature for the equals method is:

public boolean equals(Object obj)

The method returns true if the two objects are equal, and false otherwise. You should use the instanceof operator to check if obj is an instance of the class you are designing before checking the contents. This is to ensure that you can call methods that belong to that class and/or use fields that belong to that class. For example, look at the code below, which implements the equals method for the Point class:

@Override
public boolean equals(Object obj) {
		if (obj instanceof Point) {
			return (this.x == ((Point) obj.xx && (this.y == ((Point) obj).y);
		}
		return false;
}

Notice that to compare 2 Point objects, we need to check if the x and y coordinates are the same. Imagine if we passed a String into the obj parameter. Strings do not have fields called x and y, so if we didn’t use instanceof to check if obj was an instance of Point, we would’ve gotten an error. Long story short: Use instanceof to check if obj is the right class!

Triangle

Create a class called Triangle.

Note: Use Heron's Formula to calculate the area of a triangle given its 3 sides.

Note: 2 triangles are equal if their perimeters are equal.

Create a driver class called TestTriangle. In the main method, create 2 identical triangle objects, triangle1 and triangle2 (make sure the side lengths form an actual triangle). Print the result of using the equality operator on the two objects. Then print the result of calling triangle1.equals(triangle2).

Previous Section

Next Section

⚖️

Copyright © 2021 Code 4 Tomorrow. All rights reserved. The code in this course is licensed under the MIT License. If you would like to use content from any of our courses, you must obtain our explicit written permission and provide credit. Please contact classes@code4tomorrow.org for inquiries.