Introduction to Inheritance | 9.1/9.2 | 9.3/9.4 | 9.5 | 9.6 | 9.7 | Hacks |
Overriding Methods and Using the super keyword
Method Overrides
Every class inherits methods from its parent class. These methods can be called in the child class and can also be overridden.
Shape Class
This is the same shape class from before.
public class Shape {
protected String name;
private int length;
private int width;
// Default constructor
public Shape() {
this.name = "shape";
this.length = 10;
this.width = 5;
}
// Parameterized constructor
public Shape(String name, int length, int width) {
this.name = name;
this.length = length;
this.width = width;
}
// Getter methods
public String get_name() {
return this.name;
}
public int get_length() {
return this.length;
}
public int get_width() {
return this.width;
}
// Setter methods
public void set_name(String n) {
this.name = n;
}
public void set_length(int a) {
this.length = a;
}
public void set_width(int b) {
this.width = b;
}
// Method to calculate the area
public double calc_area() {
return this.length * this.width;
}
// Method to calculate the perimeter
public double calc_perimeter(){
return 2*this.length + 2*this.width;
}
// Method to print the shape
public void print_shape() {
System.out.println("Shape: " + this.name);
}
// Additional method to print something
public void print_something() {
System.out.println("This is a shape");
}
}
public class Driver {
public static void main(String[] args) {
Shape s1 = new Shape();
}
}
Creating a Triangle:
Currently Our shape class only takes in length and width parameters. This works fine for squares and rectangles, but what if we wanted to make a different shape? To define a Triangle we can use the 3 side lengths. while still inheriting the same behavior from the base Shape class.
public class Triangle extends Shape {
private int side1;
private int side2;
private int side3;
public Triangle() {
this.name = "triangle";
this.side1 = 1;
this.side2 = 2;
this.side3 = 3;
}
// Constructor that takes a name and three side lengths
public Triangle(String name, int s1, int s2, int s3) {
super(name, 0, 0); // Call to Shape constructor to set the name
this.name = "triangle";
this.side1 = s1;
this.side2 = s2;
this.side3 = s3;
}
}
Here’s our Triangle
As seen above, we’re creating a new child class: Triangle This class inherits name parameter from the parent shape class and takes in 3 new side length paramiters to define the triangle’s geometry.
Lets try to create a new triangle with the side lengths 3, 4, and 5. We can also check to see if we inherited correctly from shape by callign the calc_area and print_shape methods.
Triangle t1 = new Triangle(“triangle”, 3, 4, 5); t1.print_shape(); System.out.println(“Area of t1 = “ + t1.calc_area());
We have a problem…
We were able to make a new triangle from the inherited triangle class, however, our area is not being calculated correctly. We inherited the default shape class’ calc_area method:
area = length x width
This is defaulting to an area of 0 when we create a new triangle without specifying the length and width. Instead, we can use Heron’s formula to calculate the area of a triangle given 3 side lengths.
area = sqrt(s(s-s1)(s-s2)(s-s3)) where s is the semi-perimiter (s1+s2+s3)/2
Popcorn Hack 1
Lets re-define the Triangle class but this time override the default area method with the Heron’s formula
public class Triangle extends Shape {
private int side1;
private int side2;
private int side3;
public Triangle() {
this.name = "triangle";
this.side1 = 1;
this.side2 = 2;
this.side3 = 3;
}
public Triangle(String name, int s1, int s2, int s3) {
super(name, 0, 0); // Call to Shape constructor to set the name
this.name = "triangle";
this.side1 = s1;
this.side2 = s2;
this.side3 = s3;
}
@Override
public double calc_area() {
double s = (side1 + side2 + side3) / 2.0; // semi-perimeter
double area = Math.sqrt(s * (s - side1) * (s - side2) * (s - side3)); // Heron's formula
return area;
}
public static void main(String[] args) {
Triangle t1 = new Triangle("triangle", 3, 4, 5);
t1.print_shape();
System.out.println("Area of t1 = " + t1.calc_area()); // Expected output: 6.0
}
}
The @Override annotation tells Java that the following method will be a method override. If the following method does not override an existing method the compiler will throw an error. This is useful to catch mistakes in method name spelling and ensuring that a method override takes place. The annotation is not necessary, but is good practice.
*Note it is essential to type the name of the function to be overridden exactly
Triangle t1 = new Triangle("triangle", 3, 4, 5);
t1.print_shape();
System.out.println("Area of t1 = " + t1.calc_area());
Shape: triangle
Area of t1 = 0.0
If done correctly the area of Triangle t1 should be 6.
Important things to note
- using the final keyword in the parent method will make that method unable to be overridden
- methods can be overidden to give more access but cannot restrict acces: private -> public, but not public -> private
- use of @Override is highly encouraged
Popcorn Hack 2
re-write the Triangle sublcass so that it also overrides the calc_perimeter()
public class Triangle extends Shape {
private int side1;
private int side2;
private int side3;
public Triangle() {
this.name = "triangle";
this.side1 = 1;
this.side2 = 2;
this.side3 = 3;
}
public Triangle(String name, int s1, int s2, int s3) {
super(name, 0, 0);
this.name = "triangle";
this.side1 = s1;
this.side2 = s2;
this.side3 = s3;
}
@Override
public double calc_area() {
double s = (side1 + side2 + side3) / 2.0;
return Math.sqrt(s * (s - side1) * (s - side2) * (s - side3)); // Heron's formula
}
@Override
public double calc_perimeter() {
return side1 + side2 + side3; // Perimeter of a triangle
}
public static void main(String[] args) {
Triangle ti84 = new Triangle("triangle", 3, 4, 5);
System.out.println("Area of ti84 = " + ti84.calc_area()); // Expected output: 6.0
System.out.println("Perimeter of ti84 = " + ti84.calc_perimeter()); // Expected output: 12
}
}
Triangle ti84 = new Triangle("triangle", 3, 4, 5);
System.out.println(ti84.calc_area());
System.out.println(ti84.calc_perimeter());
0.0
0.0
Super keyword
What if we wanted to create our child triangle class, but also use overridden methods from the parent shape class? For example, what if we wanted to print out not only that our shape is a triangle, but also that it is a shape. We could accomplish this with the super keyword.
public class Triangle extends Shape {
private int side1;
private int side2;
private int side3;
public Triangle() {
this.name = "triangle";
this.side1 = 1;
this.side2 = 2;
this.side3 = 3;
}
// Constructor that takes a name and three side lengths
public Triangle(String name, int s1, int s2, int s3) {
super(name, 0, 0); // Call to Shape constructor to set the name
this.name = "triangle";
this.side1 = s1;
this.side2 = s2;
this.side3 = s3;
}
public void print_shape() {
super.print_something();
print_something();
System.out.println("Shape: " + this.name);
}
@Override
public void print_something() {
System.out.println("This is a triangle");
}
}
Triangle t2 = new Triangle("triangle", 3, 4, 5);
t2.print_shape();
This is a shape
This is a triangle
Shape: triangle
The Super Keyword
As seen above, using the super.method calls the parent class’ method. Something to note is that calling a super method inside annother method will first complete the parent method before moving on to the next line.
As seen above, the parent print something is called first, then the triangle’s print something, and lastly print shape.
Something to be aware of is not to call a method in itself without the super keyword. This will cause an error/
public class Triangle extends Shape {
private int side1;
private int side2;
private int side3;
public Triangle() {
this.name = "triangle";
this.side1 = 1;
this.side2 = 2;
this.side3 = 3;
}
// Constructor that takes a name and three side lengths
public Triangle(String name, int s1, int s2, int s3) {
super(name, 0, 0); // Call to Shape constructor to set the name
this.name = "triangle";
this.side1 = s1;
this.side2 = s2;
this.side3 = s3;
}
public void print_shape() {
print_shape();
}
@Override
public void print_something() {
System.out.println("This is a triangle");
}
}
Triangle t2 = new Triangle("triangle", 3, 4, 5);
t2.print_shape();
As seen above, this results in an infinite loop