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;
}
public abstract double calculateArea();
public void printSomething() {
System.out.println("This is a shape named: " + name);
}
}
// Rectangle subclass
class Rectangle extends Shape {
public Rectangle(String name, double length, double width) {
super(name, length, width);
}
@Override
public double calculateArea() {
return length * width;
}
@Override
public void printSomething() {
System.out.println("This is a rectangle named: " + name);
}
}
// Triangle subclass
class Triangle extends Shape {
public Triangle(String name, double base, double height) {
super(name, base, height);
}
@Override
public double calculateArea() {
return 0.5 * length * width; // base * height / 2
}
@Override
public void printSomething() {
System.out.println("This is a triangle named: " + name);
}
}
// Circle subclass
class Circle extends Shape {
public Circle(String name, double radius) {
super(name, radius, 0); // width is not used for Circle
}
@Override
public double calculateArea() {
return Math.PI * Math.pow(length, 2); // using length as radius
}
@Override
public void printSomething() {
System.out.println("This is a circle named: " + name);
}
}
// Hexagon subclass
class Hexagon extends Shape {
public Hexagon(String name, double sideLength) {
super(name, sideLength, 0); // width is not used for Hexagon
}
@Override
public double calculateArea() {
return (3 * Math.sqrt(3) / 2) * Math.pow(length, 2); // area formula for hexagon
}
@Override
public void printSomething() {
System.out.println("This is a hexagon named: " + name);
}
}
// Ellipse subclass
class Ellipse extends Shape {
public Ellipse(String name, double length, double width) {
super(name, length, width); // Call to the superclass constructor
}
@Override
public double calculateArea() {
return Math.PI * (length / 2) * (width / 2); // area formula for ellipse
}
@Override
public void printSomething() {
System.out.println("This is an ellipse named: " + name);
}
}
// Driver code to test the classes
public class Main {
public static void main(String[] args) {
Rectangle rectangle = new Rectangle("Rectangle 1", 4.0, 6.0);
Triangle triangle = new Triangle("Triangle 1", 5.0, 3.0);
Circle circle = new Circle("Circle 1", 3.0);
Hexagon hexagon = new Hexagon("Hexagon 1", 2.0);
Ellipse ellipse = new Ellipse("Ellipse 1", 4.0, 2.0);
Shape[] shapes = {rectangle, triangle, circle, hexagon, ellipse};
for (Shape shape : shapes) {
shape.printSomething();
System.out.println("Area: " + shape.calculateArea());
System.out.println();
}
}
}
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 childprint_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;
}
public abstract double calculateArea();
public abstract double calculatePerimeter();
public void printSomething() {
System.out.println("This is a shape named: " + name);
}
}
// Rectangle subclass
class Rectangle extends Shape {
public Rectangle(String name, double length, double width) {
super(name, length, width);
}
@Override
public double calculateArea() {
return length * width;
}
@Override
public double calculatePerimeter() {
return 2 * (length + width);
}
@Override
public void printSomething() {
super.printSomething(); // Call the parent method
System.out.println("This is also a rectangle.");
}
}
// Triangle subclass
class Triangle extends Shape {
public Triangle(String name, double base, double height) {
super(name, base, height);
}
@Override
public double calculateArea() {
return 0.5 * length * width; // base * height / 2
}
@Override
public double calculatePerimeter() {
return length + width + Math.sqrt(Math.pow(length, 2) + Math.pow(width, 2)); // approximate for triangle
}
@Override
public void printSomething() {
super.printSomething(); // Call the parent method
System.out.println("This is also a triangle.");
}
}
// Circle subclass
class Circle extends Shape {
public Circle(String name, double radius) {
super(name, radius, 0); // width is not used for Circle
}
@Override
public double calculateArea() {
return Math.PI * Math.pow(length, 2); // using length as radius
}
@Override
public double calculatePerimeter() {
return 2 * Math.PI * length; // using length as radius
}
@Override
public void printSomething() {
super.printSomething(); // Call the parent method
System.out.println("This is also a circle.");
}
}
// Hexagon subclass
class Hexagon extends Shape {
public Hexagon(String name, double sideLength) {
super(name, sideLength, 0); // width is not used for Hexagon
}
@Override
public double calculateArea() {
return (3 * Math.sqrt(3) / 2) * Math.pow(length, 2); // area formula for hexagon
}
@Override
public double calculatePerimeter() {
return 6 * length; // perimeter of hexagon
}
@Override
public void printSomething() {
super.printSomething(); // Call the parent method
System.out.println("This is also a hexagon.");
}
}
// Ellipse subclass
class Ellipse extends Shape {
public Ellipse(String name, double length, double width) {
super(name, length, width); // Call to the superclass constructor
}
@Override
public double calculateArea() {
return Math.PI * (length / 2) * (width / 2); // area formula for ellipse
}
@Override
public double calculatePerimeter() {
// Approximation for the perimeter of an ellipse
return Math.PI * (3 * (length / 2 + width / 2) - Math.sqrt((3 * (length / 2) + (width / 2)) * ((length / 2) + 3 * (width / 2))));
}
@Override
public void printSomething() {
super.printSomething(); // Call the parent method
System.out.println("This is also an ellipse.");
}
}
// Driver code to test the classes
public class Main {
public static void main(String[] args) {
Rectangle rectangle = new Rectangle("Rectangle 1", 4.0, 6.0);
Triangle triangle = new Triangle("Triangle 1", 5.0, 3.0);
Circle circle = new Circle("Circle 1", 3.0);
Hexagon hexagon = new Hexagon("Hexagon 1", 2.0);
Ellipse ellipse = new Ellipse("Ellipse 1", 4.0, 2.0);
Shape[] shapes = {rectangle, triangle, circle, hexagon, ellipse};
for (Shape shape : shapes) {
shape.printSomething();
System.out.println("Area: " + shape.calculateArea());
System.out.println("Perimeter: " + shape.calculatePerimeter());
System.out.println();
}
}
}
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;
}
public abstract double calculateArea(); // Abstract method for area
public abstract String draw(); // Abstract method for drawing
public void printSomething() {
System.out.println("This is a shape named: " + name);
}
}
// Triangle subclass
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 double calculateArea() {
return 0.5 * base * height; // Area = 0.5 * base * height
}
@Override
public String draw() {
return "Drawing a triangle.";
}
@Override
public void printSomething() {
super.printSomething();
System.out.println("This is also a triangle.");
}
}
// Rectangle subclass
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 double calculateArea() {
return length * width; // Area = length * width
}
@Override
public String draw() {
return "Drawing a rectangle.";
}
@Override
public void printSomething() {
super.printSomething();
System.out.println("This is also a rectangle.");
}
}
// Hexagon subclass
class Hexagon extends Shape {
private double sideLength;
public Hexagon(String name, double sideLength) {
super(name);
this.sideLength = sideLength;
}
@Override
public double calculateArea() {
return (3 * Math.sqrt(3) / 2) * Math.pow(sideLength, 2); // Area = (3√3 / 2) * s²
}
@Override
public String draw() {
return "Drawing a hexagon.";
}
@Override
public void printSomething() {
super.printSomething();
System.out.println("This is also a hexagon.");
}
}
// Driver code to test the classes
public class Main {
public static void main(String[] args) {
Shape[] shapes = {
new Triangle("Triangle 1", 5.0, 3.0),
new Rectangle("Rectangle 1", 4.0, 6.0),
new Hexagon("Hexagon 1", 2.0)
};
for (Shape shape : shapes) {
shape.printSomething();
System.out.println("Area: " + shape.calculateArea());
System.out.println(shape.draw()); // Call draw method
System.out.println();
}
}
}
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.
// Base Shape class
abstract class Shape {
protected String name;
public Shape(String name) {
this.name = name;
}
public abstract double calculateArea(); // Abstract method for area
public abstract String draw(); // Abstract method for drawing
public void printSomething() {
System.out.println("This is a shape named: " + name);
}
}
// Square subclass
class Square extends Shape {
private double sideLength;
public Square(String name, double sideLength) {
super(name);
this.sideLength = sideLength;
}
@Override
public double calculateArea() {
return sideLength * sideLength; // Area = side²
}
@Override
public String draw() {
return "Drawing a square.";
}
@Override
public void printSomething() {
super.printSomething();
System.out.println("This is also a square.");
}
}
// Driver code to demonstrate polymorphism
public class Main {
public static void main(String[] args) {
Shape myShape = new Square("Square 1", 4.0); // Polymorphic behavior
myShape.printSomething(); // Calls Square's printSomething
System.out.println("Area: " + myShape.calculateArea()); // Calls Square's calculateArea
System.out.println(myShape.draw()); // Calls Square's draw
// Down-casting example
if (myShape instanceof Square) {
Square squareShape = (Square) myShape; // Down-casting
System.out.println("Down-casted to Square: " + squareShape.draw());
}
}
}
9.7 Hacks
- Create an class where you execute an unchanged method from Object, then execute a different method from Object that you changed.
// Custom class that extends Object
class CustomObject {
private String name;
public CustomObject(String name) {
this.name = name;
}
// Overriding the toString() method from Object
@Override
public String toString() {
return "CustomObject Name: " + name;
}
// Custom method
public void customMethod() {
System.out.println("This is a custom method in CustomObject.");
}
}
// Driver class to demonstrate method execution
public class Main {
public static void main(String[] args) {
// Creating an instance of CustomObject
CustomObject myObject = new CustomObject("Example");
// Calling the unchanged method from Object
System.out.println("Unchanged Object method (toString): " + myObject.toString());
// Calling the changed method (custom method)
myObject.customMethod();
}
}
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:
- A private integer field named
edition
that represents the edition number of the textbook. - 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.
- A method
getEdition()
that returns the edition of the textbook. - 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. - An overridden method
getBookInfo()
that returns a string representation of the textbook information, including the title, price, and edition. - 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.
// Base class Book
class Book {
private String title;
private double price;
// Constructor for the 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;
}
// Method to return book information (can be overridden)
public String getBookInfo() {
return "Title: " + title + ", Price: $" + price;
}
// Overriding toString for convenient output
@Override
public String toString() {
return getBookInfo();
}
}
// Subclass Textbook extending Book
public class Textbook extends Book {
private int edition;
// Constructor for Textbook
public Textbook(String title, double price, int edition) {
super(title, price); // Call to superclass constructor
if (edition <= 0) {
throw new IllegalArgumentException("Edition must be a positive integer.");
}
this.edition = edition;
}
// Getter for edition
public int getEdition() {
return edition;
}
// Method to check if one textbook can substitute for another
public boolean canSubstituteFor(Textbook other) {
return this.getTitle().equals(other.getTitle()) && this.edition >= other.getEdition();
}
// Overridden method to get textbook info including edition
@Override
public String getBookInfo() {
return super.getBookInfo() + ", Edition: " + edition;
}
// Overriding toString for convenient display of textbook information
@Override
public String toString() {
return getBookInfo();
}
// Main method for testing
public static void main(String[] args) {
// Example usage
Textbook textbook1 = new Textbook("Java Programming", 59.99, 3);
Textbook textbook2 = new Textbook("Java Programming", 49.99, 2);
Textbook textbook3 = new Textbook("Java Programming", 39.99, 4);
System.out.println(textbook1); // Display textbook1 info
System.out.println("Can textbook1 substitute for textbook2? " + textbook1.canSubstituteFor(textbook2));
System.out.println("Can textbook1 substitute for textbook3? " + textbook1.canSubstituteFor(textbook3));
// Attempting to create a textbook with an invalid edition
try {
Textbook invalidTextbook = new Textbook("Python Programming", 45.99, -1);
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage()); // Handling the exception
}
}
}
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
d) The getArea() method doesn’t return a double
C
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()
c) toString()
d) getClass()
B