Skip to the content.
Introduction to Inheritance 9.1/9.2 9.3/9.4 9.5 9.6 9.7 Hacks

Creating and Writing Constructors for Superclasses and Subclasses

DevOps

Introduction

  • 9.1

In Java, object-oriented programming allows developers to structure programs as a collection of classes and objects. One key concept in OOP is inheritance, where a class (subclass) inherits properties and methods from another class (superclass). This allows code to be reused and models relationships between objects.

Superclass: A superclass in Java is a general class that contains common fields and methods that can be shared by its subclasses. It is also sometimes referred to as a parent class.

Subclass: A subclass (or child class) is a class that extends the functionality of a superclass. A subclass inherits all the attributes and methods of the superclass and can also have its own additional properties and methods.

You might be wondering why are subclasses and super classes useful? Well, let’s take a look at an example.

Shapes

What if we wanted to solve the problem of managing different types of shapes, each with unique properties and behaviors, but still wanted to avoid repetitive code? Well, let’s plan out a solution using inheritance. By creating a general Shape class with shared attributes, and then extending it into specific subclasses like Rectangle and Triangle, we can reuse common logic while allowing each shape to override methods like calc_area() for their specific needs. This approach simplifies the design.

How are we setting this up?

Let’s think about attributes that all shapes have. For simplicity, we can think of name, length, and width as attributes of a shape.

Let’s start setting up our main class. Here, we have a default and parametrized constructor. People can create a default shape, or pass in values to characterize their own shape.

We can also add additional methods to calculate area/print a shape.

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;
    }

}



Let’s add setters (to set variables) and getters (to get a shapes attributes like name, length, and width.)

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;
    }
}

Now we can have two subclasses, Rectangle and Triangle.

Screenshot 2024-09-18 at 12 04 51 PM

public class Rectangle extends Shape {
    public Rectangle() {
        super();
    }

    public Rectangle(String name, int length, int width) {
        super(name, length, width);
    }


}

public class Triangle extends Shape {
    public Triangle() {
        super();
    }

    public Triangle(String name, int length, int width) {
        super(name, length, width);
    }


}

You might notice the super() keyword being used a lot here. But what does it mean?

This leads us to our next big idea.

9.2 Constructors for Subclasses

  • Writing a Constructor for Subclass: When a subclass is created, it inherits the fields and methods from its superclass. However, a subclass does not inherit constructors from the superclass. If a subclass needs to initialize its own fields in addition to the fields of the superclass, it must provide its own constructor and explicitly call the constructor of the superclass using super().

  • Understanding super(): When writing a constructor for a subclass, you often need to call the constructor of the superclass to initialize the fields inherited from it. The super() call must be the first statement in the subclass constructor. If the superclass constructor requires arguments, those arguments must be passed in the super() call.

Obviously, the code blocks above are pretty basic and have a lot to be improved. For example, what if we wanted to model more complex shapes, like a circle or a hexagon? How could we extend our current Shape class to do that?

Popcorn Hack

Here’s a challenge for you: Implement two new subclasses, Circle and Hexagon, extending from the Shape class. Follow the same structure as the Rectangle and Triangle classes!

Screenshot 2024-09-18 at 12 21 38 PM

public class ShapeDemo {
    public static void main(String[] args) {
        // Create a Circle object
        Circle circle = new Circle("Circle", 5);
        System.out.println("Circle Area: " + circle.calculateArea());
        circle.print_something();

        // Create a Hexagon object
        Hexagon hexagon = new Hexagon("Hexagon", 4);
        System.out.println("Hexagon Area: " + hexagon.calculateArea());
        hexagon.print_something();
    }
}

public class Circle extends Shape {
    public Circle() {
        super();
    }

    public Circle(String name, int radius) {
        super(name, radius, 0); // Width is not needed for a circle
    }

    // Method to calculate area
    public double calculateArea() {
        int radius = this.get_length(); // Using length as the radius
        return Math.PI * radius * radius; // Area of a circle: π * r^2
    }

    // Override the print_something method
    public void print_something() {
        System.out.println("This is a Circle with radius " + this.get_length());
    }
}

public class Hexagon extends Shape {
    public Hexagon() {
        super();
    }

    public Hexagon(String name, int sideLength) {
        super(name, sideLength, 0); // Width is not needed for a hexagon
    }

    // Method to calculate area
    public double calculateArea() {
        int sideLength = this.get_length(); // Using length as the side length
        return ((3 * Math.sqrt(3)) / 2) * sideLength * sideLength; // Area of a regular hexagon
    }

    // Override the print_something method
    public void print_something() {
        System.out.println("This is a Hexagon with side length " + this.get_length());
    }
}

ShapeDemo.main(null);
Circle Area: 78.53981633974483
This is a Circle with radius 5
Hexagon Area: 41.569219381653056
This is a Hexagon with side length 4

You might notice the super() keyword being used a lot. But what does it mean?

This leads us to our next lesson.

9.2 Constructors for Subclasses

  • Writing a Constructor for Subclass: When a subclass is created, it inherits the fields and methods from its superclass. However, a subclass does not inherit constructors from the superclass. If a subclass needs to initialize its own fields in addition to the fields of the superclass, it must provide its own constructor and explicitly call the constructor of the superclass using super().

  • Understanding super(): When writing a constructor for a subclass, you often need to call the constructor of the superclass to initialize the fields inherited from it. The super() call must be the first statement in the subclass constructor. If the superclass constructor requires arguments, those arguments must be passed in the super() call.

Popcorn Hack 2 Optional: Mastering super()

What if we wanted to understand how to properly use the super() keyword in subclasses to ensure that our shapes are correctly initialized?

Here’s a fun challenge:

  1. Create a new subclass called Ellipse that extends Shape.

Constructor: Implement a constructor for Ellipse that accepts parameters for name, length, and width. This constructor should call the superclass constructor using super().

  1. Update Your Driver Code

Test the Ellipse: Instantiate an Ellipse object and print its area. Verify that the constructor correctly initializes the shape and that the super() keyword is used properly. Hints:

Ellipse Constructor: Use super(name, length, width) to initialize inherited fields. Check Order: Remember, super() must be the first statement in your subclass constructor.

public abstract 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;
    }

    // Abstract method to be overridden by subclasses
    public abstract void print_something();
}
public class Ellipse extends Shape {
    // Constructor for Ellipse
    public Ellipse(String name, int length, int width) {
        super(name, length, width); // Calling the superclass constructor
    }

    // Method to calculate area of the ellipse
    public double calculateArea() {
        int a = this.get_length(); // Semi-major axis
        int b = this.get_width();  // Semi-minor axis
        return Math.PI * a * b; // Area of ellipse: π * a * b
    }

    // Override the print_something method
    @Override
    public void print_something() {
        System.out.println("This is an Ellipse with semi-major axis " + this.get_length() + " and semi-minor axis " + this.get_width());
    }
}
public class Circle extends Shape {
    public Circle() {
        super();
    }

    public Circle(String name, int radius) {
        super(name, radius, 0); // Width is not needed for a circle
    }

    // Method to calculate area
    public double calculateArea() {
        int radius = this.get_length(); // Using length as the radius
        return Math.PI * radius * radius; // Area of a circle: π * r^2
    }

    // Override the print_something method
    @Override
    public void print_something() {
        System.out.println("This is a Circle with radius " + this.get_length());
    }
}
public class ShapeDemo {
    public static void main(String[] args) {
        // Create a Circle object
        Circle circle = new Circle("Circle", 5);
        System.out.println("Circle Area: " + circle.calculateArea());
        circle.print_something();

        // Create a Hexagon object
        Hexagon hexagon = new Hexagon("Hexagon", 4);
        System.out.println("Hexagon Area: " + hexagon.calculateArea());
        hexagon.print_something();
        
        // Create an Ellipse object
        Ellipse ellipse = new Ellipse("Ellipse", 7, 4);
        System.out.println("Ellipse Area: " + ellipse.calculateArea());
        ellipse.print_something();
    }
}
ShapeDemo.main(null);
Circle Area: 78.53981633974483
This is a Circle with radius 5
Hexagon Area: 41.569219381653056
This is a Hexagon with side length 4
Ellipse Area: 87.96459430051421
This is an Ellipse with semi-major axis 7 and semi-minor axis 4

Full Running Code Cell

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 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 Rectangle extends Shape {
    public Rectangle() {
        super();
    }

    public Rectangle(String name, int length, int width) {
        super(name, length, width);
    }

    @Override
    public double calc_area() {
        return (this.get_length() * this.get_width());
    }

    @Override
    public void print_something() {
        System.out.println("This is a rectangle");
    }
}

public class Triangle extends Shape {
    public Triangle() {
        super();
    }

    public Triangle(String name, int length, int width) {
        super(name, length, width);
    }

    @Override
    public double calc_area() {
        return (get_length() * get_width() * 0.5);
    }
}

public class Driver {
    public static void main(String[] args) {
        Shape s1 = new Shape();
        Shape s2 = new Rectangle("rectangle", 4, 10);
        Shape s3 = new Triangle("triangle", 5, 20);

        System.out.println("Area of s1 = " + s1.calc_area());
        System.out.println("Area of s2 = " + s2.calc_area());
        System.out.println("Area of s3 = " + s3.calc_area());

        s1.print_shape();
        s2.print_shape();

        // Using the overridden method
        s1.print_something();
        s2.print_something();
    }
}

// Run the driver code
Driver.main(new String[0]);

Area of s1 = 50.0
Area of s2 = 40.0
Area of s3 = 50.0
Shape: Shape
Shape: rectangle
This is a shape
This is a rectangle