Java Source File
Download
/**
* Lab 4: Shape, Circle, Rectangle, and Square - Multi-level Inheritance
* PT821 - Object-Oriented Programming
* State University of Zanzibar (SUZA)
*
* LEARNING OBJECTIVES:
* - Build a multi-level inheritance hierarchy (Shape -> Circle/Rectangle -> Square)
* - Understand the "is-a" relationship: A Square IS-A Rectangle
* - Override setters to maintain class invariants
* - Use polymorphism with a common superclass
*
* INSTRUCTIONS:
* Complete the following exercises step by step.
* Follow the TODO comments and implement the required functionality.
*/
// ============================================================
// PART A: The Shape Class (Top-level Superclass)
// ============================================================
/*
* TODO 1: Create a class called "Shape" with:
*
* Private instance variables:
* - color (String, default "red")
* - filled (boolean, default true)
*
* Constructors:
* - Shape() - default values
* - Shape(String color, boolean filled)
*
* Public methods:
* - getColor(), setColor(String color)
* - isFilled(), setFilled(boolean filled)
* - toString() - returns "Shape[color=?, filled=?]"
*/
// Write your Shape class here:
// ============================================================
// PART B: The Circle Class (Subclass of Shape)
// ============================================================
/*
* TODO 2: Create a class called "Circle" that extends Shape with:
*
* Private instance variable:
* - radius (double, default 1.0)
*
* Constructors:
* - Circle() - default values
* - Circle(double radius)
* - Circle(double radius, String color, boolean filled)
* Must call super(color, filled)
*
* Public methods:
* - getRadius(), setRadius(double radius)
* - getArea() - returns Math.PI * radius * radius
* - getPerimeter() - returns 2 * Math.PI * radius
* - @Override toString() - returns "Circle[Shape[color=?, filled=?], radius=?]"
*/
// Write your Circle class here:
// ============================================================
// PART C: The Rectangle Class (Subclass of Shape)
// ============================================================
/*
* TODO 3: Create a class called "Rectangle" that extends Shape with:
*
* Private instance variables:
* - width (double, default 1.0)
* - length (double, default 1.0)
*
* Constructors:
* - Rectangle() - default values
* - Rectangle(double width, double length)
* - Rectangle(double width, double length, String color, boolean filled)
* Must call super(color, filled)
*
* Public methods:
* - getWidth(), setWidth(double width)
* - getLength(), setLength(double length)
* - getArea() - returns width * length
* - getPerimeter() - returns 2 * (width + length)
* - @Override toString() - returns "Rectangle[Shape[color=?, filled=?], width=?, length=?]"
*/
// Write your Rectangle class here:
// ============================================================
// PART D: The Square Class (Subclass of Rectangle)
// ============================================================
/*
* TODO 4: Create a class called "Square" that extends Rectangle with:
*
* KEY CONCEPT: A Square is a special Rectangle where width == length
*
* Constructors:
* - Square() - default values (uses super() which gives 1.0 x 1.0)
* - Square(double side) - calls super(side, side)
* - Square(double side, String color, boolean filled) - calls super(side, side, color, filled)
*
* Public methods:
* - getSide() - returns getWidth() (since width == length)
* - setSide(double side) - calls setWidth(side) AND setLength(side)
*
* IMPORTANT: Override both setWidth and setLength to maintain the square invariant!
* - @Override setWidth(double side) - must also set length to same value
* Use: super.setWidth(side); super.setLength(side);
* - @Override setLength(double side) - must also set width to same value
* Use: super.setWidth(side); super.setLength(side);
*
* - @Override toString() - returns "Square[Rectangle[Shape[...], width=?, length=?]]"
*/
// Write your Square class here:
// ============================================================
// PART E: Test Driver
// ============================================================
public class Lab4_ShapeHierarchy {
public static void main(String[] args) {
System.out.println("=============================================");
System.out.println(" Lab 4: Shape Hierarchy");
System.out.println("=============================================\n");
// ----- Section 1: Creating Shape Objects -----
System.out.println("--- Section 1: Creating Objects ---");
// TODO 5: Create and test objects
// Shape s1 = new Shape("yellow", false);
// System.out.println(s1);
//
// Circle c1 = new Circle(5.0, "blue", true);
// System.out.println(c1);
// System.out.println(" Area: " + c1.getArea());
// System.out.println(" Perimeter: " + c1.getPerimeter());
//
// Rectangle r1 = new Rectangle(4.0, 6.0, "green", true);
// System.out.println(r1);
// System.out.println(" Area: " + r1.getArea());
// System.out.println(" Perimeter: " + r1.getPerimeter());
//
// Square sq1 = new Square(5.0, "orange", false);
// System.out.println(sq1);
// System.out.println(" Area: " + sq1.getArea());
// System.out.println(" Perimeter: " + sq1.getPerimeter());
// ----- Section 2: Square Invariant -----
System.out.println("\n--- Section 2: Square Invariant ---");
// TODO 6: Test that Square maintains equal sides
// Square sq2 = new Square(3.0);
// System.out.println("Initial: " + sq2);
// System.out.println("Width: " + sq2.getWidth() + ", Length: " + sq2.getLength());
//
// sq2.setWidth(7.0);
// System.out.println("\nAfter setWidth(7.0):");
// System.out.println("Width: " + sq2.getWidth() + ", Length: " + sq2.getLength());
// System.out.println("Both should be 7.0!");
//
// sq2.setLength(9.0);
// System.out.println("\nAfter setLength(9.0):");
// System.out.println("Width: " + sq2.getWidth() + ", Length: " + sq2.getLength());
// System.out.println("Both should be 9.0!");
// ----- Section 3: Polymorphism -----
System.out.println("\n--- Section 3: Polymorphism ---");
// TODO 7: Use Shape references for all objects
// Shape[] shapes = {
// new Circle(3.0, "red", true),
// new Rectangle(4.0, 5.0, "blue", false),
// new Square(6.0, "green", true),
// new Circle(7.0, "purple", true),
// new Rectangle(2.0, 8.0, "yellow", false)
// };
//
// double totalArea = 0;
// for (Shape shape : shapes) {
// System.out.println(shape);
// if (shape instanceof Circle) {
// Circle c = (Circle) shape;
// System.out.println(" -> Circle area: " + c.getArea());
// totalArea += c.getArea();
// } else if (shape instanceof Square) {
// // NOTE: Check Square BEFORE Rectangle because Square IS-A Rectangle
// Square sq = (Square) shape;
// System.out.println(" -> Square area: " + sq.getArea() + " (side=" + sq.getSide() + ")");
// totalArea += sq.getArea();
// } else if (shape instanceof Rectangle) {
// Rectangle r = (Rectangle) shape;
// System.out.println(" -> Rectangle area: " + r.getArea());
// totalArea += r.getArea();
// }
// }
// System.out.println("\nTotal area of all shapes: " + totalArea);
// ----- Section 4: Inheritance Chain -----
System.out.println("\n--- Section 4: Inheritance Chain ---");
// TODO 8: Demonstrate the full inheritance chain
// Square sq3 = new Square(4.0, "cyan", true);
// System.out.println("sq3 instanceof Square: " + (sq3 instanceof Square));
// System.out.println("sq3 instanceof Rectangle: " + (sq3 instanceof Rectangle));
// System.out.println("sq3 instanceof Shape: " + (sq3 instanceof Shape));
// System.out.println("sq3 instanceof Object: " + (sq3 instanceof Object));
//
// // All these assignments are valid (upcasting)
// Rectangle r2 = sq3; // Square IS-A Rectangle
// Shape s2 = sq3; // Square IS-A Shape
// Object o = sq3; // Everything IS-A Object
// System.out.println("\nAll upcasts work! Square -> Rectangle -> Shape -> Object");
System.out.println("\n=============================================");
System.out.println(" End of Lab 4");
System.out.println("=============================================");
}
}
/*
* EXPECTED OUTPUT (after completing all TODOs):
*
* =============================================
* Lab 4: Shape Hierarchy
* =============================================
*
* --- Section 1: Creating Objects ---
* Shape[color=yellow, filled=false]
* Circle[Shape[color=blue, filled=true], radius=5.0]
* Area: 78.53981633974483
* Perimeter: 31.41592653589793
* Rectangle[Shape[color=green, filled=true], width=4.0, length=6.0]
* Area: 24.0
* Perimeter: 20.0
* Square[Rectangle[Shape[color=orange, filled=false], width=5.0, length=5.0]]
* Area: 25.0
* Perimeter: 20.0
*
* --- Section 2: Square Invariant ---
* Initial: Square[Rectangle[Shape[color=red, filled=true], width=3.0, length=3.0]]
* Width: 3.0, Length: 3.0
*
* After setWidth(7.0):
* Width: 7.0, Length: 7.0
* Both should be 7.0!
*
* After setLength(9.0):
* Width: 9.0, Length: 9.0
* Both should be 9.0!
*
* --- Section 3: Polymorphism ---
* ... (shapes with their areas)
*
* --- Section 4: Inheritance Chain ---
* sq3 instanceof Square: true
* sq3 instanceof Rectangle: true
* sq3 instanceof Shape: true
* sq3 instanceof Object: true
*
* All upcasts work! Square -> Rectangle -> Shape -> Object
*
* QUESTIONS TO ANSWER:
* 1. Why is it important to override setWidth() and setLength() in Square?
* 2. What would go wrong if Square didn't override these setters?
* 3. In Section 3, why must we check "instanceof Square" before "instanceof Rectangle"?
* 4. Is it a good design for Square to extend Rectangle? What are the pros and cons?
* (Hint: Research the "Liskov Substitution Principle")
*
* SUBMISSION:
* - Complete all TODO sections (create Shape, Circle, Rectangle, Square + uncomment test code)
* - Answer all questions above in comments
* - Submit the completed .java file
*/