• 9.6 Hacks
  • 9.7 Hacks
  • Multiple Choice
  • 9.1 Hacks

    • 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!

    • Optional!
    • Create a new subclass called Ellipse that extends Shape. Update Your Driver Code

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

    • 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
    abstract class Shape {
        protected String name;
        protected double length;
        protected double width;
    
        public Shape(String name, double length, double width) {
            this.name = name;
            this.length = length;
            this.width = width;
        }
    
        // Abstract method for area calculation
        public abstract double area();
    
        // Abstract method for printing something
        public abstract void printSomething();
    }
    
    // Circle class extending Shape
    class Circle extends Shape {
        public Circle(String name, double radius) {
            // For a circle, both length and width can be treated as the radius
            super(name, radius, radius);
        }
    
        @Override
        public double area() {
            return Math.PI * length * length;
        }
    
        @Override
        public void printSomething() {
            System.out.println("This is a circle named " + name + " with radius " + length + ".");
        }
    }
    
    // Hexagon class extending Shape
    class Hexagon extends Shape {
        public Hexagon(String name, double sideLength) {
            // For a hexagon, both length and width can be treated as the side length
            super(name, sideLength, sideLength);
        }
    
        @Override
        public double area() {
            return (3 * Math.sqrt(3) * length * length) / 2;
        }
    
        @Override
        public void printSomething() {
            System.out.println("This is a hexagon named " + name + " with side length " + length + ".");
        }
    }
    
    
    
    // Main driver class to test the shapes
    public class Main {
        public static void main(String[] args) {
            // Instantiate Circle, Hexagon, and Ellipse objects
            Shape circle = new Circle("MyCircle", 5);
            Shape hexagon = new Hexagon("MyHexagon", 6);
            Shape ellipse = new Ellipse("MyEllipse", 4, 2);
    
            // Test Circle
            circle.printSomething();
            System.out.println("Circle Area: " + circle.area());
    
            // Test Hexagon
            hexagon.printSomething();
            System.out.println("Hexagon Area: " + hexagon.area());
        }
    }
    
    Main.main(null);
    
    
    This is a circle named MyCircle with radius 5.0.
    Circle Area: 78.53981633974483
    This is a hexagon named MyHexagon with side length 6.0.
    Hexagon Area: 93.53074360871938
    

    9.3 Hacks

    • When overriding the area method for both the Ellipse and the Hexagon use the @Override notation.
    • For the area of the hexagon it may be useful to look into the hexagon area formula:

    Area = (3√3 / 2) * s²

    • Also override the calc_perimeter() method for both shapes.
    • use the super keyword to have both shaps use both the parent print_something() and child print_something() method Both shapes should print out something along the lines of: “This is a shape and also a hexagon”
    // Base Shape class
    abstract class Shape {
        protected String name;
        protected double length;
        protected double width;
    
        public Shape(String name, double length, double width) {
            this.name = name;
            this.length = length;
            this.width = width;
        }
    
        // Abstract method for area calculation
        public abstract double area();
    
        // Abstract method for perimeter calculation
        public abstract double calc_perimeter();
    
        // Abstract method for printing something
        public void printSomething() {
            System.out.println("This is a shape named " + name + ".");
        }
    }
    
    // Circle class extending Shape
    class Circle extends Shape {
        public Circle(String name, double radius) {
            // For a circle, both length and width can be treated as the radius
            super(name, radius, radius);
        }
    
        @Override
        public double area() {
            return Math.PI * length * length;
        }
    
        @Override
        public double calc_perimeter() {
            return 2 * Math.PI * length;
        }
    
        @Override
        public void printSomething() {
            super.printSomething();
            System.out.println("This is also a circle with radius " + length + ".");
        }
    }
    
    // Hexagon class extending Shape
    class Hexagon extends Shape {
        public Hexagon(String name, double sideLength) {
            // For a hexagon, both length and width can be treated as the side length
            super(name, sideLength, sideLength);
        }
    
        @Override
        public double area() {
            // Area of a hexagon: (3√3 / 2) * sideLength²
            return (3 * Math.sqrt(3) * length * length) / 2;
        }
    
        @Override
        public double calc_perimeter() {
            return 6 * length;
        }
    
        @Override
        public void printSomething() {
            super.printSomething();
            System.out.println("This is also a hexagon with side length " + length + ".");
        }
    }
    
    // Ellipse class extending Shape
    class Ellipse extends Shape {
        public Ellipse(String name, double length, double width) {
            // For an ellipse, length is the semi-major axis, and width is the semi-minor axis
            super(name, length, width);
        }
    
        @Override
        public double area() {
            // Area of an ellipse: π * length * width
            return Math.PI * length * width;
        }
    
        @Override
        public double calc_perimeter() {
            // Approximation for the perimeter of an ellipse
            return Math.PI * (3 * (length + width) - Math.sqrt((3 * length + width) * (length + 3 * width)));
        }
    
        @Override
        public void printSomething() {
            super.printSomething();
            System.out.println("This is also an ellipse with length " + length + " and width " + width + ".");
        }
    }
    
    // Main driver class to test the shapes
    public class Main {
        public static void main(String[] args) {
            // Instantiate Circle, Hexagon, and Ellipse objects
            Shape circle = new Circle("MyCircle", 5);
            Shape hexagon = new Hexagon("MyHexagon", 6);
            Shape ellipse = new Ellipse("MyEllipse", 4, 2);
    
            // Test Circle
            circle.printSomething();
            System.out.println("Circle Area: " + circle.area());
            System.out.println("Circle Perimeter: " + circle.calc_perimeter());
    
            // Test Hexagon
            hexagon.printSomething();
            System.out.println("Hexagon Area: " + hexagon.area());
            System.out.println("Hexagon Perimeter: " + hexagon.calc_perimeter());
    
            // Test Ellipse
            ellipse.printSomething();
            System.out.println("Ellipse Area: " + ellipse.area());
            System.out.println("Ellipse Perimeter: " + ellipse.calc_perimeter());
        }
    }
    
    Main.main(null);
    
    
    This is a shape named MyCircle.
    This is also a circle with radius 5.0.
    Circle Area: 78.53981633974483
    Circle Perimeter: 31.41592653589793
    This is a shape named MyHexagon.
    This is also a hexagon with side length 6.0.
    Hexagon Area: 93.53074360871938
    Hexagon Perimeter: 36.0
    This is a shape named MyEllipse.
    This is also an ellipse with length 4.0 and width 2.0.
    Ellipse Area: 25.132741228718345
    Ellipse Perimeter: 19.376842195342576
    

    9.5 Hacks

    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. Make sure your implementation returns a unique string for the Triangle class. This exercise will help reinforce how subclasses can extend functionality.

    Advanced Challenge: Area Calculation

    Now, let’s enhance our Shape class to include an area calculation feature. Modify the Shape class to include an area method, and implement it in your subclasses. Below is a structure to help you get started: Ensure each subclass calculates and returns its area correctly. This will allow you to practice method overriding further and understand how different shapes can extend base functionalities.

    Homework Hack

    For your homework, create your own class hierarchy for shapes. You should have a base class called Shape with subclasses Triangle, Rectangle, and Hexagon. Each subclass should implement a method called draw(), returning a unique string for each shape type.

    - `Triangle`: "Drawing a triangle."
    
    - `Rectangle`: "Drawing a rectangle."
    
    - `Hexagon`: "Drawing a hexagon."
    

    Make sure to demonstrate polymorphism by creating an array of Shape types and iterating through it to call the draw() method. This will reinforce your understanding of class hierarchies and method overriding.

    // Base Shape class
    abstract class Shape {
        protected String name;
    
        public Shape(String name) {
            this.name = name;
        }
    
        // Abstract draw method to be implemented by subclasses
        public abstract String draw();
    
        // Abstract method for calculating the area
        public abstract double area();
    }
    
    // Triangle class extending Shape
    class Triangle extends Shape {
        private double base;
        private double height;
    
        public Triangle(String name, double base, double height) {
            super(name);
            this.base = base;
            this.height = height;
        }
    
        @Override
        public String draw() {
            return "Drawing a triangle.";
        }
    
        @Override
        public double area() {
            return 0.5 * base * height;
        }
    }
    
    // Rectangle class extending Shape
    class Rectangle extends Shape {
        private double length;
        private double width;
    
        public Rectangle(String name, double length, double width) {
            super(name);
            this.length = length;
            this.width = width;
        }
    
        @Override
        public String draw() {
            return "Drawing a rectangle.";
        }
    
        @Override
        public double area() {
            return length * width;
        }
    }
    
    // Hexagon class extending Shape
    class Hexagon extends Shape {
        private double side;
    
        public Hexagon(String name, double side) {
            super(name);
            this.side = side;
        }
    
        @Override
        public String draw() {
            return "Drawing a hexagon.";
        }
    
        @Override
        public double area() {
            return (3 * Math.sqrt(3) * side * side) / 2;
        }
    }
    
    // Main driver class
    public class Main {
        public static void main(String[] args) {
            // Create an array of different shapes
            Shape[] shapes = {
                new Triangle("Triangle1", 3, 4),
                new Rectangle("Rectangle1", 5, 6),
                new Hexagon("Hexagon1", 7)
            };
    
            // Iterate through the array and call the draw() and area() methods
            for (Shape shape : shapes) {
                System.out.println(shape.draw());
                System.out.println("Area: " + shape.area());
            }
        }
    }
    
    Main.main(null);
    
    
    Drawing a triangle.
    Area: 6.0
    Drawing a rectangle.
    Area: 30.0
    Drawing a hexagon.
    Area: 127.30573435631248
    

    9.6 Hacks

    • using a previous example of inheritance create an example of polymorphsim, or create an example of polymorphic behavhoir between two classes of Shape and Sqaure
    • Using the previous polymorphism popcorn hack, explain which parts are static and dynamic data types and when that is the case
    • Define Down-Casting in your own words
    • using the previous polymorphism example add an example of down-casting.
    // Superclass Shape
    class Shape {
        public void draw() {
            System.out.println("Drawing a Shape");
        }
    }
    
    // Subclass Square
    class Square extends Shape {
        @Override
        public void draw() {
            System.out.println("Drawing a Square");
        }
    }
    
    // Main class to demonstrate polymorphism
    public class Main {
        public static void main(String[] args) {
            Shape myShape = new Shape();  // Static data type: Shape
            Shape mySquare = new Square(); // Static data type: Shape, Dynamic data type: Square
    
            myShape.draw();   // Calls Shape's draw method
            mySquare.draw();  // Calls Square's draw method (dynamic binding)
        }
    }
    
    
    Main.main(null);
    
    Drawing a Shape
    Drawing a Square
    

    9.7 Hacks

    • Create an class where you execute an unchanged method from Object, then execute a different method from Object that you changed.
    public class CustomClass {
        // Overriding the toString() method from Object
        @Override
        public String toString() {
            return "This is an overridden toString method!";
        }
        
        // Main method to test the execution
        public static void main(String[] args) {
            // Create an instance of the class
            CustomClass myObject = new CustomClass();
            
            // Call the unchanged hashCode method from Object
            int originalHashCode = myObject.hashCode();
            System.out.println("Unchanged hashCode from Object class: " + originalHashCode);
            
            // Call the overridden toString method
            String customToString = myObject.toString();
            System.out.println("Overridden toString method: " + customToString);
        }
    }
    
    CustomClass.main(null);
    
    Unchanged hashCode from Object class: 525511500
    Overridden toString method: This is an overridden toString method!
    

    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.

    Write the complete implementation of the Textbook class, including all specified methods and any additional features you believe would be beneficial.

    public class Textbook extends Book {
        // Private field for the edition number
        private int edition;
    
        // Constructor for Textbook
        public Textbook(String title, double price, int edition) {
            super(title, price); // Call the constructor of the superclass Book
            if (edition <= 0) {
                throw new IllegalArgumentException("Edition must be a positive integer.");
            }
            this.edition = edition;
        }
    
        // Getter method for edition
        public int getEdition() {
            return edition;
        }
    
        // Method to check if this textbook can substitute for another textbook
        public boolean canSubstituteFor(Textbook other) {
            return this.getTitle().equals(other.getTitle()) && this.edition >= other.getEdition();
        }
    
        // Override the getBookInfo method to include the edition
        @Override
        public String getBookInfo() {
            return super.getBookInfo() + ", Edition: " + edition;
        }
    
        // Override toString for easy output
        @Override
        public String toString() {
            return getBookInfo();
        }
    }
    
    

    Multiple Choice

    1. What is wrong with this block of code?

    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());
    

    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 // This is the answer

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

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

    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();
    

    a) typeOfWater()

    b) isSalty() // This is the answer

    c) toString()

    d) getClass()