Introduction to Inheritance | 9.1/9.2 | 9.3/9.4 | 9.5 | 9.6 | 9.7 | Hacks |
Hacks
Hacks Menu
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.
//Hack - Dinesh Sahai
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 print_something() {
System.out.println("This is a generic shape.");
}
}
class Circle extends Shape {
private double radius;
public Circle(String name, double radius) {
super(name, radius, radius);
this.radius = radius;
}
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
@Override
public void print_something() {
System.out.println("This is a circle!");
}
}
class Hexagon extends Shape {
private double side;
public Hexagon(String name, double side) {
super(name, side, side);
this.side = side;
}
@Override
public double calculateArea() {
return ((3 * Math.sqrt(3)) / 2) * side * side;
}
@Override
public void print_something() {
System.out.println("This is a hexagon!");
}
}
class Ellipse extends Shape {
public Ellipse(String name, double length, double width) {
super(name, length, width);
}
@Override
public double calculateArea() {
return Math.PI * (length / 2) * (width / 2);
}
@Override
public void print_something() {
System.out.println("This is an ellipse!");
}
}
public class ShapeTest {
public static void main(String[] args) {
Ellipse ellipse = new Ellipse("My Ellipse", 10, 5);
System.out.println("Area of Ellipse: " + ellipse.calculateArea());
ellipse.print_something();
Circle circle = new Circle("My Circle", 7);
System.out.println("Area of Circle: " + circle.calculateArea());
circle.print_something();
Hexagon hexagon = new Hexagon("My Hexagon", 6);
System.out.println("Area of Hexagon: " + hexagon.calculateArea());
hexagon.print_something();
}
}
ShapeTest.main(null);
Area of Ellipse: 39.269908169872416
This is an ellipse!
Area of Circle: 153.93804002589985
This is a circle!
Area of Hexagon: 93.53074360871938
This is a hexagon!
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”
//Hack - Dinesh Sahai
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 calc_perimeter();
public void print_something() {
System.out.println("This is a shape.");
}
}
class Circle extends Shape {
private double radius;
public Circle(String name, double radius) {
super(name, radius, radius);
this.radius = radius;
}
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
@Override
public double calc_perimeter() {
return 2 * Math.PI * radius;
}
@Override
public void print_something() {
System.out.println("This is a circle!");
}
}
class Hexagon extends Shape {
private double side;
public Hexagon(String name, double side) {
super(name, side, side);
this.side = side;
}
@Override
public double calculateArea() {
return ((3 * Math.sqrt(3)) / 2) * side * side;
}
@Override
public double calc_perimeter() {
return 6 * side;
}
@Override
public void print_something() {
super.print_something();
System.out.println("This is also a hexagon.");
}
}
class Ellipse extends Shape {
public Ellipse(String name, double length, double width) {
super(name, length, width);
}
@Override
public double calculateArea() {
return Math.PI * (length / 2) * (width / 2);
}
@Override
public double calc_perimeter() {
double a = length / 2;
double b = width / 2;
return Math.PI * (3 * (a + b) - Math.sqrt((3 * a + b) * (a + 3 * b)));
}
@Override
public void print_something() {
super.print_something();
System.out.println("This is also an ellipse.");
}
}
public class ShapeTest {
public static void main(String[] args) {
Ellipse ellipse = new Ellipse("My Ellipse", 10, 5);
System.out.println("Area of Ellipse: " + ellipse.calculateArea());
System.out.println("Perimeter of Ellipse: " + ellipse.calc_perimeter());
ellipse.print_something();
Hexagon hexagon = new Hexagon("My Hexagon", 6);
System.out.println("Area of Hexagon: " + hexagon.calculateArea());
System.out.println("Perimeter of Hexagon: " + hexagon.calc_perimeter());
hexagon.print_something();
}
}
ShapeTest.main(null);
Area of Ellipse: 39.269908169872416
Perimeter of Ellipse: 24.22105274417822
This is a shape.
This is also an ellipse.
Area of Hexagon: 93.53074360871938
Perimeter of Hexagon: 36.0
This is a shape.
This is also a hexagon.
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.
//9.5 Hack - Dinesh Sahai
abstract class Shape {
public abstract double area();
public abstract String draw();
}
class Triangle extends Shape {
private double base;
private double height;
public Triangle(double base, double height) {
this.base = base;
this.height = height;
}
@Override
public double area() {
return 0.5 * base * height;
}
@Override
public String draw() {
return "This is a triangle with base: " + base + " and height: " + height;
}
}
public class ShapeTest {
public static void main(String[] args) {
Triangle myTriangle = new Triangle(5, 10);
System.out.println("Area of the triangle: " + myTriangle.area());
System.out.println(myTriangle.draw());
}
}
ShapeTest.main(null);
Area of the triangle: 25.0
This is a triangle with base: 5.0 and height: 10.0
//Homework Hack - Dinesh Sahai
abstract class Shape {
public abstract String draw();
}
class Triangle extends Shape {
@Override
public String draw() {
return "Drawing a triangle.";
}
}
class Rectangle extends Shape {
@Override
public String draw() {
return "Drawing a rectangle.";
}
}
class Hexagon extends Shape {
@Override
public String draw() {
return "Drawing a hexagon.";
}
}
public class ShapeTest {
public static void main(String[] args) {
Shape[] shapes = new Shape[3];
shapes[0] = new Triangle();
shapes[1] = new Rectangle();
shapes[2] = new Hexagon();
for (Shape shape : shapes) {
System.out.println(shape.draw());
}
}
}
ShapeTest.main(null);
Drawing a triangle.
Drawing a rectangle.
Drawing a hexagon.
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.
//Hack - Dinesh Sahai
abstract class Shape {
protected String name;
public Shape(String name) {
this.name = name;
}
public abstract double calculateArea();
public void print_something() {
System.out.println("This is a shape.");
}
}
class Square extends Shape {
private double side;
public Square(String name, double side) {
super(name);
this.side = side;
}
@Override
public double calculateArea() {
return side * side;
}
@Override
public void print_something() {
System.out.println("This is a square.");
}
}
public class PolymorphismAndDownCastingExample {
public static void main(String[] args) {
Shape shape1 = new Square("My Square", 4);
shape1.print_something();
System.out.println("Area of shape1: " + shape1.calculateArea());
if (shape1 instanceof Square) {
Square square1 = (Square) shape1;
System.out.println("Area of square1: " + square1.calculateArea());
}
}
}
PolymorphismAndDownCastingExample.main(null);
This is a square.
Area of shape1: 16.0
Area of square1: 16.0
9.7 Hacks
- Create an class where you execute an unchanged method from Object, then execute a different method from Object that you changed.
//Hack - Dinesh Sahai
class MyClass {
private String name;
public MyClass(String name) {
this.name = name;
}
@Override
public String toString() {
return "MyClass Name: " + name;
}
}
public class ObjectMethodsExample {
public static void main(String[] args) {
MyClass myObject = new MyClass("Example Object");
MyClass anotherObject = new MyClass("Example Object");
System.out.println("Are both objects equal? " + myObject.equals(anotherObject));
System.out.println(myObject.toString());
}
}
ObjectMethodsExample.main(null);
Are both objects equal? false
MyClass Name: Example Object
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.
//Hack - Dinesh Sahai
class Book {
private String title;
private double price;
public Book(String title, double price) {
this.title = title;
this.price = price;
}
public String getTitle() {
return title;
}
public double getPrice() {
return price;
}
public String getBookInfo() {
return "Title: " + title + ", Price: $" + price;
}
}
public class Textbook extends Book {
private int edition;
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;
}
public int getEdition() {
return edition;
}
public boolean canSubstituteFor(Textbook other) {
if (other == null) return false; // Handle null case
return this.getTitle().equals(other.getTitle()) && this.edition >= other.edition;
}
@Override
public String getBookInfo() {
return super.getBookInfo() + ", Edition: " + edition;
}
@Override
public String toString() {
return getBookInfo();
}
}
public class TextbookTest {
public static void main(String[] args) {
try {
Textbook textbook1 = new Textbook("Calculus", 59.99, 3);
Textbook textbook2 = new Textbook("Calculus", 49.99, 2);
Textbook textbook3 = new Textbook("Physics", 39.99, 1);
System.out.println("Edition of textbook1: " + textbook1.getEdition());
System.out.println("Can textbook1 substitute for textbook2? " + textbook1.canSubstituteFor(textbook2));
System.out.println("Can textbook1 substitute for textbook3? " + textbook1.canSubstituteFor(textbook3));
System.out.println("Textbook1 Info: " + textbook1.getBookInfo());
System.out.println(textbook1);
Textbook invalidTextbook = new Textbook("Invalid Book", 29.99, -1);
} catch (IllegalArgumentException e) {
System.err.println("Error: " + e.getMessage());
}
}
}
TextbookTest.main(null);
Edition of textbook1: 3
Can textbook1 substitute for textbook2? true
Can textbook1 substitute for textbook3? false
Textbook1 Info: Title: Calculus, Price: $59.99, Edition: 3
Title: Calculus, Price: $59.99, Edition: 3
Error: Edition must be a positive integer.
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
The correct answer is: C. The toString() method must be public.
Explanation: In Java, every class implicitly extends the Object class, which contains a toString() method. To override this method and provide a custom string representation of the object, the toString() method in your class must be declared as public. In your original code, the toString() method is declared as private, which prevents it from being accessible outside the Shape class. As a result, it will not override the default toString() method from the Object class, leading to the default behavior instead of the custom implementation. Making the method public allows it to be properly invoked when you print the object or call toString() on it.
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()
The correct answer is: isSalty()
Explanation: The isSalty() method in the Water class is declared as private, meaning it is not accessible outside of the Water class itself, including in its subclass Lake. Even though the Lake class defines its own isSalty() method, this method cannot be invoked through a reference of type Water (like myLakeWater), because private methods are not inherited and are not visible outside their defining class.
In contrast, methods such as toString() and typeOfWater() are public, allowing them to be called on the myLakeWater reference, which points to an instance of Lake. Therefore, the only method that cannot be executed from the Water reference is isSalty().