Introduction
What are Abstract Classes? An Abstract class acts as a way to define methods and variables that can be inherited to form a specific relationship. Abstract classes are a powerful aspect of Object Oriented Programing, as they allow us to define a single super class which has key traits necessary for multiple subclasses, but unlike normal inheritance an Abstract class by itself cannot be an Object. In addition to Abstract Classes, we will also be discussing a close relative of Abstract Classes known as Interfaces, which are very similar to Abstract Classes but with a few key differences.
Abstract Class Use
Abstract Classes are defined in code as follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
abstract class Shape {
protected Point position;
abstract double computeArea();
public Point getPosition() {
return this.position;
}
public void setPosition(Point position) {
this.position = position;
}
public void movePositionRelative(Point position) {
double x = this.position.getX() + position.getX();
double y = this.position.getY() + position.getY();
this.position.setX(x);
this.position.setY(y);
}
}
Abstract classes act similarly to inheritance, but are defined differently and include methods labeled as abstract.
The computeArea() method has the word abstract before it, which tells the system that when another class inherits from the Shape class, that class will complete any method labeled as abstract in the Shape class.
This property allows you to define methods needed by any class inheriting from your abstract class, but gives the subclass the task of completing the method in a way that makes sense for that subclass.
For example, let’s say we have 3 subclasses, inheriting from the above Shape class: Circle, Square, Triangle. All 3 of these shapes have unique way of computing their area, pi*r^2, side^2, (1/2)*base*height, respectively. By making a method to compute area abstract, we force the user to define their own version of computeArea, potentially avoiding accidentally forgetting to define computeArea or trying to use values defined in shape that the subclass may not have access to. The purpose of an abstract class is to build a base outline for a class which can include some code, but really serves to relate multiple subclasses together.
So how do you define an abstract class? The first step of an abstract class is its definition. Below is the simple outline of an abstract class.
Rather than using public class example we replace the public with abstract. The class also has two abstract methods and a normal method. When a class inherits from this abstract class, the subclass will be expected to complete the abstract methods with their definitions as written, meaning if it takes any parameters then the subclass’s version will need to take the same parameters.
Interfaces
Interfaces are quite similar to abstract classes with a few differences. First and foremost, the definition of an interface is a little different rather than using public/abstract class name, as interfaces use public interface name.
Second, rather than using the extends keyword, a class using an interface uses the keyword implements. Finally, a class can implement multiple interfaces, rather than just one inheritance. Below is an example of defining an interface.
The interface here provides a sample method required by any implementing class, but it also defines a variable pi.
Unlike an abstract class, an interface can declare global static variables that can be implemented without any problem.
If you tried to perform this using an abstract class, the Java compiler would throw an error.
An interface is a powerful tool to use when you want to define a simple outline/blueprint for classes that may share key traits. It allows you to define common methods needed for shared functionality across multiple classes. When a class implements an interface, that class is making a contract with the interface saying that it will write all the provided methods in the interface, within the class itself.
Part 3 Key Differences between Interfaces and Abstract Classes
Now at first glance, both of these types of classes sound quite similar, but there are some key differences between the two.
Interfaces:
- Can be implemented any number of times as long as one does not overwrite another’s method
- Allow you to include public static final global variables
- Should only include abstract methods - no implemented methods
Abstract Classes:
- Can include both abstract and non-abstract methods
- May contain non-final variables
- Can provide implementation of interface
For example, if you have a superclass called Shape, and you implement the Comparable interface, then all subclasses of Shape will be required to complete the Comparable interfaces methods, allowing you to effectively inherit multiple interfaces without the need to include it in each individual subclass.
For more explanation of differences, check out this outside reference.
For the Assignment
You will be completing code for a Shape Abstract Class and 3 subclasses, Triangle, Rectangle, and Circle. Shape defines two int variables: x and y, which represent the coordinates of the center of the shape. Shape also defines 2 abstract methods calculateArea() and calculateCircumference(). Both return doubles representing the area and circumference of the shapes respectively.
For each shape you will have to create the necessary values need to calculate each of these values and getters and setters for each of the values. You will also need to create constructors for each class, and the instructions for those will be included with each class.
Provided below is a UML diagram that gives a visual representation of this lab.
Part 1 Shape: calculateArea() and calculateCircumference()
In the Shape Class you will be writing two abstract methods calculateArea() and calculateCircumference(). Both of these methods will take no parameters and will return a double. Since these methods are abstract, the other classes will define these methods for their specific class.
Part 2 Circle Class
For this part, you will be writing the Circle class. This class will have a constructor, accessors and mutators, and the abstract methods defined for this class.
Circle Class: Constructor, Circle(double radius)
The Circle class constructor will take in a double that represents a radius of a circle. This value should be stored in an instance variable that is private, since we will use mutators and accessors to manipulate and obtain the value of it.
Circle Class: Accessors and Mutators, getRadius() setRadius(double radius)
With the radius instance variable made, accessors and mutators should be made for that variable.
The accessor will return the value of the radius instance variable, and should be named getRadius().
The mutator should take in a double that can change the radius for a given Circle object, and should be named setRadius().
Circle Class: Abstract Methods, calculateArea() and calculateCircumference()
Since this class extends the Shape class, this means that the Circle class must implement the abstract methods in the Shape class. In this case, those methods are calculateArea() and calculateCircumference(). For each method, return the area and circumference respectively of a circle with the radius specified by the object.
Testing Circle Class
In the Test Class, you can now test the code written in this class. Make sure to see if the accessors and mutators are returning correctly. Also, make sure the area and circumference are being returned correctly. For area and circumference, you may assume the results will have 2 decimal points of precision.
Part 2 Rectangle Class
For this part, you will be writing the Rectangle class. This class, like the previous, will have a constructor, accessors and mutators, and abstract methods defined for this class.
Rectangle Class: Constructor, Rectangle(double height, double width)
The Rectangle class constructor will take in two doubles, one for height and the other for width. These values should be stored in instance variables.
Rectangle Class: Accessors and Mutators, getHeight() getWidth() setHeight(double height) setWidth(double width)
With the instance variables for both height and width made, accessors and mutators for each of the variables should be made. For the accessors, the method should return the value for the appropriate instance variable. For mutators, each mutator will take in a double to change the instance variables. This means there is one accessor and mutator for each instance variable.
Rectangle Class: Abstract Methods, calculateArea() and calculateCircumference()
Like in the Circle class, the Rectangle class also extends the Shape class meaning the abstract methods in the Shape class must be defined in the Rectangle class. Again, these methods are calculateArea() and calculateCircumference(). It may be obvious to you that rectangles do not have circumferences but perimeters. For the calculateCircumference() method, return the perimeter of a given rectangle object. Also, return the area of a given rectangle in the calculateArea() method.
Testing Rectangle Class
In the Test Class, you can now test the code written in this class. Make sure to see if the accessors and mutators are returning correctly. Also, make sure the area and circumference are being returned correctly. For area and circumference, you may assume the results will have 2 decimal points of precision.
Part 3 Triangle Class
For this part you will be writing the Triangle class. As in the previous class, the Triangle Class has a constructor, accessors and mutators and abstract methods defined.
Triangle Class: Constructor, Triangle(double height, double base)
The constructor for the Triangle Class will take two doubles, one for base and one for height. These values should be stored in instance variables.
Triangle Class: Accessors and Mutators, getHeight() getBase() setHeight(double height) setBase(double base)
Like in the Rectangle class, there are now two instance variables, one for height and another for base. Both of these instance variables need to have accessors and mutators. The accessors should return the respective value of an instance variable. The mutators will take a double to change a respective instance variable.
Triangle Class: Abstract Methods, calculateArea() and calculateCircumference()
As seen previously in all the other subclasses, the Triangle class also extends the Shape class. This means that the abstract methods in the Shape class have to be defined in the Triangle class.
The Triangle class will be limited to performing calculations for equilateral triangles, where all sides are the same length. This should make calculations for area and perimeter relatively simple.
The methods that must be implemented are calculateArea() and calculateCircumference(). For calculateArea(), return the area of a triangle, which as a reminder is (base*height)/2. As before with the Rectangle Class, there is not a circumference for a triangle. Therefore, for this method return the perimeter of a triangle. As you have the base of the triangle, and because this class is limited to just equilateral triangles, you should be able to easily calculate the perimeter of the triangle..
Testing Triangle Class
In the Test Class, you can now test the code written in this class. Make sure to see if the accessors and mutators are returning correctly. Also, make sure the area and circumference are being returned correctly.