Skip to the content.

Unit 9 Hacks

Popcorn Hack

Here’s a challenge for you: Implement two new subclasses, Circle and Hexagon, extending from the Shape class. Each shape should have a method to calculate its area and should override the print_something() method to print something unique for that shape. Follow the same structure as the Rectangle and Triangle classes!

// Base Shape class
public abstract class Shape {
    private String name;
    private int dimension1;
    private int dimension2;

    public Shape() {
    }

    public Shape(String name, int dimension1, int dimension2) {
        this.name = name;
        this.dimension1 = dimension1;
        this.dimension2 = dimension2;
    }

    public String getName() {
        return name;
    }

    public int get_dimension1() {
        return dimension1;
    }

    public int get_dimension2() {
        return dimension2;
    }

    public abstract double calc_area();
    public abstract void print_something();
}

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

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

    public double calc_area() {
        return (this.get_dimension1() * this.get_dimension2());
    }

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

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

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

    public double calc_area() {
        return (get_dimension1() * get_dimension2() * 0.5);
    }

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

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

    public Circle(String name, int radius) {
        super(name, radius, 0);  // Only one dimension is needed, so we set the second to 0
    }

    public double calc_area() {
        int radius = get_dimension1();
        return Math.PI * radius * radius;
    }

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

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

    public Hexagon(String name, int sideLength) {
        super(name, sideLength, 0);  // Only one dimension is needed, so we set the second to 0
    }

    public double calc_area() {
        int sideLength = get_dimension1();
        return (3 * Math.sqrt(3) / 2) * sideLength * sideLength;
    }

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

// Main class to test the shapes
public class Main {
    public static void main(String[] args) {
        Shape rectangle = new Rectangle("Rectangle", 4, 5);
        Shape triangle = new Triangle("Triangle", 4, 5);
        Shape circle = new Circle("Circle", 3);
        Shape hexagon = new Hexagon("Hexagon", 6);

        // Testing Rectangle
        System.out.println(rectangle.getName() + " Area: " + rectangle.calc_area());
        rectangle.print_something();

        // Testing Triangle
        System.out.println(triangle.getName() + " Area: " + triangle.calc_area());
        triangle.print_something();

        // Testing Circle
        System.out.println(circle.getName() + " Area: " + circle.calc_area());
        circle.print_something();

        // Testing Hexagon
        System.out.println(hexagon.getName() + " Area: " + hexagon.calc_area());
        hexagon.print_something();
    }
}
Main.main(null);
Rectangle Area: 20.0
This is a rectangle.
Triangle Area: 10.0
This is a triangle.
Circle Area: 28.274333882308138
This is a circle.
Hexagon Area: 93.53074360871938
This is a hexagon.

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(). Area Calculation: Override the calc_area() method to compute the area of an ellipse using the formula: Area = π * (length/2) * (width/2). Custom Message: Override the print_something() method to print a unique message for the Ellipse.

  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.

// Base Shape class
public abstract class Shape {
    private String name;
    private int dimension1;
    private int dimension2;

    public Shape() {
    }

    public Shape(String name, int dimension1, int dimension2) {
        this.name = name;
        this.dimension1 = dimension1;
        this.dimension2 = dimension2;
    }

    public String getName() {
        return name;
    }

    public int get_dimension1() {
        return dimension1;
    }

    public int get_dimension2() {
        return dimension2;
    }

    public abstract double calc_area();
    public abstract void print_something();
}

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

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

    public double calc_area() {
        return (this.get_dimension1() * this.get_dimension2());
    }

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

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

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

    public double calc_area() {
        return (get_dimension1() * get_dimension2() * 0.5);
    }

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

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

    public Circle(String name, int radius) {
        super(name, radius, 0);  // Only one dimension is needed, so we set the second to 0
    }

    public double calc_area() {
        int radius = get_dimension1();
        return Math.PI * radius * radius;
    }

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

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

    public Hexagon(String name, int sideLength) {
        super(name, sideLength, 0);  // Only one dimension is needed, so we set the second to 0
    }

    public double calc_area() {
        int sideLength = get_dimension1();
        return (3 * Math.sqrt(3) / 2) * sideLength * sideLength;
    }

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

// Ellipse class
public class Ellipse extends Shape {
    public Ellipse(String name, int length, int width) {
        // Call the superclass (Shape) constructor to initialize the fields
        super(name, length, width);
    }

    @Override
    public double calc_area() {
        // Calculate the area of an ellipse: π * (length/2) * (width/2)
        double semiMajorAxis = get_dimension1() / 2.0;
        double semiMinorAxis = get_dimension2() / 2.0;
        return Math.PI * semiMajorAxis * semiMinorAxis;
    }

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

// Main class to test the shapes
public class Main {
    public static void main(String[] args) {
        Shape rectangle = new Rectangle("Rectangle", 4, 5);
        Shape triangle = new Triangle("Triangle", 4, 5);
        Shape circle = new Circle("Circle", 3);
        Shape hexagon = new Hexagon("Hexagon", 6);
        Shape ellipse = new Ellipse("Ellipse", 10, 6);  // New ellipse object

        // Testing Rectangle
        System.out.println(rectangle.getName() + " Area: " + rectangle.calc_area());
        rectangle.print_something();

        // Testing Triangle
        System.out.println(triangle.getName() + " Area: " + triangle.calc_area());
        triangle.print_something();

        // Testing Circle
        System.out.println(circle.getName() + " Area: " + circle.calc_area());
        circle.print_something();

        // Testing Hexagon
        System.out.println(hexagon.getName() + " Area: " + hexagon.calc_area());
        hexagon.print_something();

        // Testing Ellipse
        System.out.println(ellipse.getName() + " Area: " + ellipse.calc_area());
        ellipse.print_something();
    }
}
Main.main(null);
Rectangle Area: 20.0
This is a rectangle.
Triangle Area: 10.0
This is a triangle.
Circle Area: 28.274333882308138
This is a circle.
Hexagon Area: 93.53074360871938
This is a hexagon.
Ellipse Area: 47.12388980384689
This is an ellipse.

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 3

Lets re-define the Triangle class but this time override the default area method with the Heron’s formula

// Base Shape class
public abstract class Shape {
    private String name;
    private int dimension1;
    private int dimension2;
    private int dimension3;

    public Shape() {
    }

    public Shape(String name, int dimension1, int dimension2, int dimension3) {
        this.name = name;
        this.dimension1 = dimension1;
        this.dimension2 = dimension2;
        this.dimension3 = dimension3;
    }

    public String getName() {
        return name;
    }

    public int get_dimension1() {
        return dimension1;
    }

    public int get_dimension2() {
        return dimension2;
    }

    public int get_dimension3() {
        return dimension3;
    }

    public abstract double calc_area();
    public abstract void print_something();
}

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

    public Rectangle(String name, int length, int width) {
        super(name, length, width, 0);  // No third dimension needed for a rectangle
    }

    public double calc_area() {
        return (this.get_dimension1() * this.get_dimension2());
    }

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

// Redefine the Triangle class using Heron's formula
public class Triangle extends Shape {
    public Triangle() {
        super();
    }

    public Triangle(String name, int side1, int side2, int side3) {
        super(name, side1, side2, side3);  // All three sides are passed as dimensions
    }

    @Override
    public double calc_area() {
        int a = get_dimension1();
        int b = get_dimension2();
        int c = get_dimension3();

        // Calculate semi-perimeter
        double s = (a + b + c) / 2.0;

        // Use Heron's formula to calculate the area
        return Math.sqrt(s * (s - a) * (s - b) * (s - c));
    }

    @Override
    public void print_something() {
        System.out.println("This is a triangle, calculated using Heron's formula.");
    }
}

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

    public Circle(String name, int radius) {
        super(name, radius, 0, 0);  // Only one dimension is needed, so we set the other two to 0
    }

    public double calc_area() {
        int radius = get_dimension1();
        return Math.PI * radius * radius;
    }

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

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

    public Hexagon(String name, int sideLength) {
        super(name, sideLength, 0, 0);  // Only one dimension is needed, so we set the other two to 0
    }

    public double calc_area() {
        int sideLength = get_dimension1();
        return (3 * Math.sqrt(3) / 2) * sideLength * sideLength;
    }

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

// Ellipse class
public class Ellipse extends Shape {
    public Ellipse(String name, int length, int width) {
        // Call the superclass (Shape) constructor to initialize the fields
        super(name, length, width, 0);  // No third dimension needed for an ellipse
    }

    @Override
    public double calc_area() {
        // Calculate the area of an ellipse: π * (length/2) * (width/2)
        double semiMajorAxis = get_dimension1() / 2.0;
        double semiMinorAxis = get_dimension2() / 2.0;
        return Math.PI * semiMajorAxis * semiMinorAxis;
    }

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

// Main class to test the shapes
public class Main {
    public static void main(String[] args) {
        Shape rectangle = new Rectangle("Rectangle", 4, 5);
        Shape triangle = new Triangle("Triangle", 3, 4, 5);  // Sides of the triangle
        Shape circle = new Circle("Circle", 3);
        Shape hexagon = new Hexagon("Hexagon", 6);
        Shape ellipse = new Ellipse("Ellipse", 10, 6);  // New ellipse object

        // Testing Rectangle
        System.out.println(rectangle.getName() + " Area: " + rectangle.calc_area());
        rectangle.print_something();

        // Testing Triangle
        System.out.println(triangle.getName() + " Area: " + triangle.calc_area());
        triangle.print_something();

        // Testing Circle
        System.out.println(circle.getName() + " Area: " + circle.calc_area());
        circle.print_something();

        // Testing Hexagon
        System.out.println(hexagon.getName() + " Area: " + hexagon.calc_area());
        hexagon.print_something();

        // Testing Ellipse
        System.out.println(ellipse.getName() + " Area: " + ellipse.calc_area());
        ellipse.print_something();
    }
}
Main.main(null);
Rectangle Area: 20.0
This is a rectangle.
Triangle Area: 6.0
This is a triangle, calculated using Heron's formula.
Circle Area: 28.274333882308138
This is a circle.
Hexagon Area: 93.53074360871938
This is a hexagon.
Ellipse Area: 47.12388980384689
This is an ellipse.

Popcorn Hack 4

re-write the Triangle sublcass so that it also overrides the calc_perimeter()

// Abstract Shape class
public abstract class Shape {
    private String name;
    private int dimension1;
    private int dimension2;
    private int dimension3;

    // Default constructor
    public Shape() {}

    // Constructor with parameters
    public Shape(String name, int dimension1, int dimension2, int dimension3) {
        this.name = name;
        this.dimension1 = dimension1;
        this.dimension2 = dimension2;
        this.dimension3 = dimension3;
    }

    // Getters for dimensions and name
    public String getName() {
        return name;
    }

    public int get_dimension1() {
        return dimension1;
    }

    public int get_dimension2() {
        return dimension2;
    }

    public int get_dimension3() {
        return dimension3;
    }

    // Abstract methods
    public abstract double calc_area();
    public abstract void print_something();
}

// Triangle class extending Shape
public class Triangle extends Shape {
    private int side1;
    private int side2;
    private int side3;

    // Default constructor
    public Triangle() {
        super("triangle", 0, 0, 0); // Call to super constructor to set name and placeholder dimensions
        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, 0); // Call to super constructor to set the name and placeholder dimensions
        this.side1 = s1;
        this.side2 = s2;
        this.side3 = s3;
    }

    // Override the calc_area method to use Heron's formula
    @Override
    public double calc_area() {
        double s = (side1 + side2 + side3) / 2.0;  // Semi-perimeter
        return Math.sqrt(s * (s - side1) * (s - side2) * (s - side3));
    }

    // Override the calc_perimeter method
    public double calc_perimeter() {
        return side1 + side2 + side3;
    }

    // Override print_something
    @Override
    public void print_something() {
        System.out.println("This is a triangle with perimeter: " + calc_perimeter() + " and area: " + calc_area());
    }
}

// Main class to test the functionality
public class Main {
    public static void main(String[] args) {
        // Instantiate a triangle with sides 3, 4, and 5
        Triangle ti84 = new Triangle("triangle", 3, 4, 5);
        
        // Print the area and perimeter of the triangle
        System.out.println("Area: " + ti84.calc_area());
        System.out.println("Perimeter: " + ti84.calc_perimeter());
        
        // Call the print_something method
        ti84.print_something();
    }
}
Main.main(null);
Area: 6.0
Perimeter: 12.0
This is a triangle with perimeter: 12.0 and area: 6.0

Popcorn Hack 5

  • Create an example of Polymorphism in your own project.

If you want some more information and examples of Polymorphism see the examples further down

// Parent class Car
public class Car {
    protected String brand;
    protected String model;

    // Constructor
    public Car(String brand, String model) {
        this.brand = brand;
        this.model = model;
    }

    // Method to be overridden by subclasses
    public void drive() {
        System.out.println("The car is driving...");
    }

    // Common method for all cars
    public void showDetails() {
        System.out.println("Brand: " + this.brand + ", Model: " + this.model);
    }
}

// SportsCar subclass
public class SportsCar extends Car {

    public SportsCar(String brand, String model) {
        super(brand, model);
    }

    // Override drive method for SportsCar
    @Override
    public void drive() {
        System.out.println("The sports car is driving fast!");
    }
}

// Sedan subclass
public class Sedan extends Car {

    public Sedan(String brand, String model) {
        super(brand, model);
    }

    // Override drive method for Sedan
    @Override
    public void drive() {
        System.out.println("The sedan is cruising smoothly...");
    }
}

// Truck subclass
public class Truck extends Car {

    public Truck(String brand, String model) {
        super(brand, model);
    }

    // Override drive method for Truck
    @Override
    public void drive() {
        System.out.println("The truck is hauling a heavy load.");
    }
}

// Main class to demonstrate polymorphism
public class Main {
    public static void main(String[] args) {
        // Polymorphism: An array of Car references, each referring to different car types
        Car[] cars = {
            new SportsCar("Ferrari", "488"),
            new Sedan("Toyota", "Camry"),
            new Truck("Ford", "F-150")
        };

        // Iterate over the array and call the drive method for each car
        for (Car car : cars) {
            car.showDetails();  // Common method
            car.drive();        // Polymorphic method
            System.out.println();
        }
    }
}
Main.main(null);
Brand: Ferrari, Model: 488
The sports car is driving fast!

Brand: Toyota, Model: Camry
The sedan is cruising smoothly...

Brand: Ford, Model: F-150
The truck is hauling a heavy load.

Practical Exercise: Popcorn Hack 6

Let’s implement the Triangle subclass to deepen your understanding. Below is a half-completed method for the Triangle class. Your task is to complete the draw method:

// Base class Shape
class Shape {
    // Method to draw a generic shape
    public String draw() {
        return "Drawing a shape";
    }
}

// Subclass Triangle extending Shape
class Triangle extends Shape {
    // Override the draw method to provide specific behavior for Triangle
    @Override
    public String draw() {
        return "Drawing a triangle";  // Unique message for triangle
    }
}

// Main class to test the implementation
public class Main {
    public static void main(String[] args) {
        // Create an instance of Triangle but treat it as a Shape (polymorphism)
        Shape myTriangle = new Triangle();
        
        // Call the draw method and print the result
        System.out.println(myTriangle.draw()); // Should output: "Drawing a triangle."
    }
}
Main.main(null);
Drawing a triangle

Popcorn Hacks 7

Define down-casting in your own words

add an example of down-casting to your previous polymorphism example

down-casting - is the process of converting a reference of a parent class type into a reference of a child class type. This is typically done when you want to access specific methods or properties that are available in the subclass but not in the parent class.

// Parent class Car
public class Car {
    protected String brand;
    protected String model;

    // Constructor
    public Car(String brand, String model) {
        this.brand = brand;
        this.model = model;
    }

    // Method to be overridden by subclasses
    public void drive() {
        System.out.println("The car is driving...");
    }

    // Common method for all cars
    public void showDetails() {
        System.out.println("Brand: " + this.brand + ", Model: " + this.model);
    }
}

// SportsCar subclass
public class SportsCar extends Car {

    public SportsCar(String brand, String model) {
        super(brand, model);
    }

    // Override drive method for SportsCar
    @Override
    public void drive() {
        System.out.println("The sports car is driving fast!");
    }

    // Unique method only in SportsCar
    public void activateTurbo() {
        System.out.println("Turbo mode activated!");
    }
}

// Sedan subclass
public class Sedan extends Car {

    public Sedan(String brand, String model) {
        super(brand, model);
    }

    // Override drive method for Sedan
    @Override
    public void drive() {
        System.out.println("The sedan is cruising smoothly...");
    }
}

// Truck subclass
public class Truck extends Car {

    public Truck(String brand, String model) {
        super(brand, model);
    }

    // Override drive method for Truck
    @Override
    public void drive() {
        System.out.println("The truck is hauling a heavy load.");
    }
}

// Main class to demonstrate polymorphism and down-casting
public class Main {
    public static void main(String[] args) {
        // Polymorphism: An array of Car references, each referring to different car types
        Car[] cars = {
            new SportsCar("Ferrari", "488"),
            new Sedan("Toyota", "Camry"),
            new Truck("Ford", "F-150")
        };

        // Iterate over the array and call the drive method for each car
        for (Car car : cars) {
            car.showDetails();  // Common method
            car.drive();        // Polymorphic method

            // Down-casting: Checking if the car is a SportsCar and accessing SportsCar-specific methods
            if (car instanceof SportsCar) {
                SportsCar sportsCar = (SportsCar) car;  // Down-casting
                sportsCar.activateTurbo();  // Access SportsCar-specific method
            }

            System.out.println();
        }
    }
}
Main.main(null);
Brand: Ferrari, Model: 488
The sports car is driving fast!
Turbo mode activated!

Brand: Toyota, Model: Camry
The sedan is cruising smoothly...

Brand: Ford, Model: F-150
The truck is hauling a heavy load.

Popcorn Hack 8

Create an example where you execute an unchanged method from Object, then execute a different method from Object that you changed.

// A custom class that inherits from Object (all classes in Java do implicitly)
public class MyClass {
    private String name;
    private int value;

    // Constructor
    public MyClass(String name, int value) {
        this.name = name;
        this.value = value;
    }

    // Overriding the toString method from Object to provide custom string output
    @Override
    public String toString() {
        return "MyClass: name = " + name + ", value = " + value;
    }

    // Main method to demonstrate unchanged and changed Object methods
    public static void main(String[] args) {
        // Create an instance of MyClass
        MyClass obj = new MyClass("Sample", 42);

        // 1. Execute an unchanged method from Object: getClass()
        System.out.println("Executing unchanged getClass() method:");
        System.out.println("Class: " + obj.getClass());  // Should print the class name with package

        // 2. Execute a changed method from Object: toString()
        System.out.println("\nExecuting overridden toString() method:");
        System.out.println(obj.toString());  // Should print the custom string defined in toString()
    }
}
MyClass.main(null);

Executing unchanged getClass() method:
Class: class REPL.$JShell$30$MyClass

Executing overridden toString() method:
MyClass: name = Sample, value = 42

FRQ Prompt

Consider a program that manages a collection of books, specifically focusing on textbooks. You are required to implement a class named Textbook that extends an existing class called Book. The Textbook class should include the following features:

  1. A private integer field named edition that represents the edition number of the textbook.
  2. A constructor that takes three parameters: a string for the title, a double for the price, and an integer for the edition. This constructor should invoke the superclass constructor to initialize the title and price.
  3. A method getEdition() that returns the edition of the textbook.
  4. A method canSubstituteFor(Textbook other) that determines if the current textbook can be substituted for another textbook. This method should return true if both textbooks have the same title and the current textbook’s edition is equal to or greater than the other textbook’s edition.
  5. An overridden method getBookInfo() that returns a string representation of the textbook information, including the title, price, and edition.
  6. Optional: Include error handling in the constructor to ensure that the edition is a positive integer, and override the toString() method for convenient output of the textbook information.
  7. Write the complete implementation of the Textbook class, including all specified methods and any additional features you believe would be beneficial.
// Assuming the existence of a superclass Book
public class Book {
    private String title;
    private double price;

    // Constructor for Book class
    public Book(String title, double price) {
        this.title = title;
        this.price = price;
    }

    // Getter for title
    public String getTitle() {
        return title;
    }

    // Getter for price
    public double getPrice() {
        return price;
    }

    // A placeholder method to be overridden in the subclass
    public String getBookInfo() {
        return "Title: " + title + ", Price: $" + price;
    }
}

// Textbook class extending the Book class
public class Textbook extends Book {
    private int edition;

    // Constructor for Textbook class, calling the superclass constructor
    public Textbook(String title, double price, int edition) {
        super(title, price);
        if (edition <= 0) {
            throw new IllegalArgumentException("Edition must be a positive integer.");
        }
        this.edition = edition;
    }

    // Method to get the edition of the textbook
    public int getEdition() {
        return edition;
    }

    // Method to determine if the current textbook can substitute for another textbook
    public boolean canSubstituteFor(Textbook other) {
        // Check if titles are the same and current edition is greater or equal
        return this.getTitle().equals(other.getTitle()) && this.edition >= other.getEdition();
    }

    // Overridden method to provide a string representation of the textbook
    @Override
    public String getBookInfo() {
        return super.getBookInfo() + ", Edition: " + edition;
    }

    // Overridden toString() method for easier printing of textbook info
    @Override
    public String toString() {
        return "Textbook[Title: " + getTitle() + ", Price: $" + getPrice() + ", Edition: " + edition + "]";
    }

    // Main method for testing
    public static void main(String[] args) {
        // Create two textbook objects for testing
        Textbook textbook1 = new Textbook("Intro to Computer Science", 59.99, 3);
        Textbook textbook2 = new Textbook("Intro to Computer Science", 49.99, 2);

        // Test getBookInfo() method
        System.out.println(textbook1.getBookInfo());  // Expected: Title: Intro to Computer Science, Price: $59.99, Edition: 3

        // Test canSubstituteFor method
        System.out.println(textbook1.canSubstituteFor(textbook2));  // Expected: true (edition 3 >= edition 2)

        // Test toString method
        System.out.println(textbook1);  // Expected: Textbook[Title: Intro to Computer Science, Price: $59.99, Edition: 3]
    }
}
Textbook.main(null);
Title: Intro to Computer Science, Price: $59.99, Edition: 3
true
Textbook[Title: Intro to Computer Science, Price: $59.99, Edition: 3]

Multiple Choice

1. What is wrong with this block of code?

a) You can’t use the this keyword in the constructor

b) When passing a double through an argument it must be in the form of 0.0

c) The toString() method must be public

d) The getArea() method doesn’t return a double

class Shape{
    private double length = 0;
    private double width = 0;
    
    public Shape(double length, double width){
        this.length = length;
        this.width = width;
    }

    public double getArea(){
        return this.length * this.width;
    }

    private String toString(){
        return "Shape length:"+ (new Double(this.length)).toString() + " width:" + (new Double(this.width)).toString();
    }
}

Shape myShape = new Shape(2,3);

System.out.println(myShape.getArea());

Answer: c) The toString() method must be public

2. Which method cannot be exectuted in the following example of Polymorphism

a) typeOfWater()

b) isSalty()

c) toString()

d) getClass()

class Water{
    public String toString(){
        return "Water";
    }

    private boolean isSalty(){
        return false;
    }

    public String typeOfWater(){
        return "Static";
    }

}

class Lake extends Water{
    public String toString(){
        return "Lake";
    }

    public boolean isSalty(){
        return true;
    }
}

Water myLakeWater = new Lake();

Answer: b) isSalty()