Introduction
In previous labs we have covered Abstract classes and Interfaces. Beyond acting as blueprints to build new classes off of, abstract classes and Interfaces serve a special purpose in linking multiple classes together. We can use a type of interface or abstract class to act as a way to store multiple different types of objects, who use that interface or abstract class. In this lab you will learn how to use ArrayLists to store Objects through the use of inheritance, learn a new type of for loop, and learn about “Polymorphism”.
ArrayList and inheritance
An ArrayList is a powerful tool that allows you to store a large number of objects without having to preallocate memory. This tool becomes enhanced with the inclusion of inheritance. Although an abstract class cannot be instantiated normally, it can be used as the argument to an ArrayList. When used in this style, the ArrayList can now store any type of Object who inherits from the Abstract class used in the declaration. Lets say we have an Abstract class called Shape, we can declare an arrayList as such
This list now has the ability to store any object that inherits from the Shape class. The same methodology can be applied to interfaces; any class which implements a specific interface can be stored in an Array or ArrayList of that type of interface. Where this really comes in to play is with “Polymorphism”.
Polymorphism
Polymorphism refers to determining which program behavior to execute depending on data types. Method overloading is a form of compile-time polymorphism wherein the compiler determines which of several identically-named methods to call based on the method’s arguments. Another form is runtime polymorphism wherein the compiler cannot make the determination but instead the determination is made while the program is running. The below is an example of Runtime polymorphism. Even though each object is stored as a shape the compiler is able to determine which version of Calculate area to choose and which version of getClass (a method provided by the Object base class) to use.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.util.ArrayList;
public class test {
public static void main(String[] args) {
ArrayList<Shape> list = new ArrayList<Shape>();
list.add(new Circle(12, 2, 2));
list.add(new Rectangle(13, 21, 2, 2));
list.add(new Triangle(12, 10, 2, 2));
list.add(new Circle(5, 5, 5));
list.add(new Rectangle(5, 5, 2, 2));
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i).getClass() + ": " + list.get(i).calculateArea());
}
}
}
/**
* Outputs:
* class Circle: 452.3893421169302
* class Rectangle: 273.0
* class Triangle: 60.0
* class Circle: 78.53981633974483
* class Rectangle: 25.0
**/
Polymorphism is a powerful tool when use properly, however there are a few common mistakes when attempting to use it. Polymorphism only works with methods that are available to the base class being used. For example, let’s assume we have a method called volume in the Circle class that calculates the volume of a sphere. Let’s also assume that the volume method is only in the Circle class, not the base class (Shape). If we attempt to call the volume method on our Shape objects, our program will become very upset, because there is no volume method accessible to the Shape class. One way we can fix this is by first casting our Shape object to a Circle, and then using the volume method on the Circle object we have created.
Enhanced For Loops
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.util.ArrayList;
public class test {
public static void main(String[] args) {
ArrayList<Shape> list = new ArrayList<Shape>();
list.add(new Circle(12, 2, 2));
list.add(new Rectangle(13, 21, 2, 2));
list.add(new Triangle(12, 10, 2, 2));
list.add(new Circle(5, 5, 5));
list.add(new Rectangle(5, 5, 2, 2));
//foreach loop
for (Shape s : list) {
System.out.println(s.getClass() + ": " + s.calculateArea());
}
}
}
The enhanced for loop, or often called a foreach loop, is a special loop that allows you to iterate over each object in an array and use it individually. A for each loop can be defined as such
In the above example we use the variable s to represent each Shape object inside of the list.
This gives you a variable that you can use in place of saying, array[i] or list.get(i). This style of for loop is also useful when applying the concepts of Polymorphism. Although we used a generic variable Shape s, the compiler knows that we cannot actually instantiate a Shape object. Instead we use Shape to refer to any of the subclasses of Shape, and can invoke any of their inherited methods or variables without the need to type cast.
Lab Instructions
You will be completing the class PolymorphismPractice. Shape, Circle, and Rectangle have already been written for you. Provided below is a UML diagram that gives a visual representation of this lab.
Step 1
In the main method, initialize an ArrayList that holds objects of type Shape.
Step 2
Let’s now fill the ArrayList with 5 Circles and 5 Rectangles (for a total of 10 objects). A for loop is definitely helpful in this step, and also consider using Math.random().
Testing
To test this step, you can use the ArrayList size() method to make sure it has a size of 10, or try printing out all 10 items in the ArrayList.
Step 3
Let’s complete the getInfo method. Start by editing this method header for getInfo so that takes in an ArrayList of Shape objects as a parameter. Now we will be iterating over the list and adding information to a StringBuilder. For this method use a foreach loop to iterate through the list. For each shape in the array, add the shape information to your StringBuilder HINT use the toString method for that Object.
Let’s also add the lines “Has area: the area of the shape”, as well as “Has circumference: the circumference of the shape”. Please make sure that the area and circumference found are rounded to two decimal places. Separate every object with a new line character. Finally, return the String of all the information found.
An example of how this should look when printed:
Testing
Call and print the getInfo method inside your main method.
Step 4
Let’s now complete the findCircles method. Start by editing this method header for findCircles so that it takes in an ArrayList of Shape objects as a parameter. Now, using another for each loop, and the instanceof keyword add the information of ONLY Circles to a StringBuilder (make sure every Circle Object’s info is separated by a new line). HINT use the toString method
Finally return a string holding all of the different Circle’s information.
An example of how this should look when printed:
Testing
Call and print the findCircles method inside your main method.