| 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 1
public class Shape {
protected String name;
private int length;
private int width;
// Default constructor
public Shape() {
this.name = "Shape";
this.length = 10;
this.width = 5;
}
// Parameterized constructor
public Shape(String name, int length, int width) {
this.name = name;
this.length = length;
this.width = width;
}
// Getter methods
public String get_name() {
return this.name;
}
public int get_length() {
return this.length;
}
public int get_width() {
return this.width;
}
// Setter methods
public void set_name(String n) {
this.name = n;
}
public void set_length(int a) {
this.length = a;
}
public void set_width(int b) {
this.width = b;
}
}
public class Circle extends Shape {
public Circle() {
super(); // Calls the default constructor of Shape
}
public Circle(String name, int radius) {
super(name, radius, radius); // Using length and width to represent radius for simplicity
}
}
public class Hexagon extends Shape {
public Hexagon() {
super(); // Calls the default constructor of Shape
}
public Hexagon(String name, int sideLength) {
super(name, sideLength, sideLength); // Using length and width to represent the side length
}
}
// optional hack:
// Ellipse:
public class Ellipse extends Shape {
// Constructor that takes name, length, and width as parameters
public Ellipse(String name, int length, int width) {
super(name, length, width); // Calls the superclass constructor
}
// Method to calculate the area of the ellipse
public double getArea() {
// Area formula: π * (length / 2) * (width / 2)
return Math.PI * (get_length() / 2.0) * (get_width() / 2.0);
}
}
// Driver Code:
public class Main {
public static void main(String[] args) {
// Instantiate an Ellipse object
Ellipse myEllipse = new Ellipse("My Ellipse", 10, 6);
// Print the Ellipse's details and area
System.out.println("Shape Name: " + myEllipse.get_name());
System.out.println("Length (Major Axis): " + myEllipse.get_length());
System.out.println("Width (Minor Axis): " + myEllipse.get_width());
System.out.println("Area of Ellipse: " + myEllipse.getArea());
}
}
9.3 Hacks
- When overriding the area method for both the Ellipse and the Hexagon use the
@Overridenotation. - 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 1:
public class Shape {
protected String name;
private int length;
private int width;
// Default constructor
public Shape() {
this.name = "shape";
this.length = 10;
this.width = 5;
}
// Parameterized constructor
public Shape(String name, int length, int width) {
this.name = name;
this.length = length;
this.width = width;
}
// Getter methods
public String get_name() {
return this.name;
}
public int get_length() {
return this.length;
}
public int get_width() {
return this.width;
}
// Setter methods
public void set_name(String n) {
this.name = n;
}
public void set_length(int a) {
this.length = a;
}
public void set_width(int b) {
this.width = b;
}
// Method to calculate the area
public double calc_area() {
return this.length * this.width;
}
// Method to calculate the perimeter
public double calc_perimeter(){
return 2*this.length + 2*this.width;
}
// Method to print the shape
public void print_shape() {
System.out.println("Shape: " + this.name);
}
// Additional method to print something
public void print_something() {
System.out.println("This is a shape");
}
}
public class Driver {
public static void main(String[] args) {
Shape s1 = new Shape();
}
}
public class Triangle extends Shape {
private int side1;
private int side2;
private int side3;
public Triangle() {
this.name = "triangle";
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); // Call to Shape constructor to set the name
this.name = "triangle";
this.side1 = s1;
this.side2 = s2;
this.side3 = s3;
}
}
public class Triangle extends Shape {
private int side1;
private int side2;
private int side3;
public Triangle() {
this.name = "triangle";
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); // Call to Shape constructor to set the name
this.name = "triangle";
this.side1 = s1;
this.side2 = s2;
this.side3 = s3;
}
@Override
public double calc_area() {
double s = (side1 + side2 + side3) / 2.0; // Semi-perimeter
double area = Math.sqrt(s * (s - side1) * (s - side2) * (s - side3)); // Heron's formula
return area;
}
}
Triangle t1 = new Triangle("triangle", 3, 4, 5);
t1.print_shape();
System.out.println("Area of t1 = " + t1.calc_area());
Shape: triangle
Area of t1 = 6.0
// hack 2:
public class Triangle extends Shape {
private int side1;
private int side2;
private int side3;
// Default constructor
public Triangle() {
super("triangle", 0, 0); // Call to Shape constructor to set the name
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); // Call to Shape constructor to set the name
this.side1 = s1;
this.side2 = s2;
this.side3 = s3;
}
// Override the calc_area method to implement Heron's formula
@Override
public double calc_area() {
double s = (side1 + side2 + side3) / 2.0; // Semi-perimeter
double area = Math.sqrt(s * (s - side1) * (s - side2) * (s - side3)); // Heron's formula
return area;
}
// Override the calc_perimeter method to compute the perimeter of the triangle
@Override
public double calc_perimeter() {
return side1 + side2 + side3; // Sum of the three sides
}
}
Triangle ti84 = new Triangle("triangle", 3, 4, 5);
System.out.println(ti84.calc_area());
System.out.println(ti84.calc_perimeter());
6.0
12.0
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.
// advanced hack:
class Shape {
public String draw() {
return "Drawing a shape";
}
public double area() {
return 0; // Default implementation
}
}
class Circle extends Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double area() {
return Math.PI * radius * radius; // Area = πr²
}
@Override
public String draw() {
return "Drawing a circle.";
}
}
class Square extends Shape {
private double side;
public Square(double side) {
this.side = side;
}
@Override
public double area() {
return side * side; // Area = side²
}
@Override
public String draw() {
return "Drawing a square.";
}
}
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; // Area = 1/2 * base * height
}
@Override
public String draw() {
return "Drawing a triangle.";
}
}
class Rectangle extends Shape {
private double length;
private double width;
public Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
@Override
public double area() {
return length * width; // Area = length * width
}
@Override
public String draw() {
return "Drawing a rectangle.";
}
}
// Main class to demonstrate the functionality
public class Main {
public static void main(String[] args) {
Shape[] shapes = new Shape[]{
new Circle(5),
new Square(4),
new Triangle(3, 6),
new Rectangle(4, 5)
};
for (Shape shape : shapes) {
System.out.println(shape.draw());
System.out.println("Area: " + shape.area());
}
}
}
// hw hack:
// Base class
class Shape {
public String draw() {
return "Drawing a shape";
}
}
// Subclass Triangle
class Triangle extends Shape {
@Override
public String draw() {
return "Drawing a triangle.";
}
}
// Subclass Rectangle
class Rectangle extends Shape {
@Override
public String draw() {
return "Drawing a rectangle.";
}
}
// Subclass Hexagon
class Hexagon extends Shape {
@Override
public String draw() {
return "Drawing a hexagon.";
}
}
public class Main {
public static void main(String[] args) {
// Create an array of Shape objects using polymorphism
Shape[] shapes = {new Triangle(), new Rectangle(), new Hexagon()};
// Iterate through the array and call the draw() method
for (Shape shape : shapes) {
System.out.println(shape.draw());
}
}
}
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.
Down-casting is the process of casting a reference variable of a superclass type to a subclass type. This allows access to the specific methods and properties defined in the subclass that are not available in the superclass. However, down-casting should be done cautiously, as it can lead to a ClassCastException at runtime if the object being cast does not actually belong to the subclass.
class MediaPlayer {
public String play() {
return "Playing media.";
}
}
class AudioPlayer extends MediaPlayer {
@Override
public String play() {
return "Playing audio file.";
}
public String audioSpecificMethod() {
return "This is an audio-specific method.";
}
}
class VideoPlayer extends MediaPlayer {
@Override
public String play() {
return "Playing video file.";
}
}
class ImageViewer extends MediaPlayer {
@Override
public String play() {
return "Displaying image file.";
}
}
public class Main {
public static void main(String[] args) {
MediaPlayer[] players = {new AudioPlayer(), new VideoPlayer(), new ImageViewer()};
for (MediaPlayer player : players) {
System.out.println(player.play());
// Down-casting example
if (player instanceof AudioPlayer) {
AudioPlayer audioPlayer = (AudioPlayer) player; // Down-casting
System.out.println(audioPlayer.audioSpecificMethod());
}
}
}
}
9.7 Hacks
- Create an class where you execute an unchanged method from Object, then execute a different method from Object that you changed.
class Animal {
// Unchanged method from Object
@Override
public String toString() {
return "This is an animal.";
}
}
class Dog extends Animal {
// Overriding the toString method
@Override
public String toString() {
return "This is a dog.";
}
public void displayClassName() {
// Unchanged method from Object
System.out.println("Class name: " + this.getClass().getSimpleName());
}
}
public class Main {
public static void main(String[] args) {
Animal myDog = new Dog();
// Execute the overridden toString method
System.out.println(myDog.toString()); // Output: This is a dog.
// Execute the unchanged method from Object
((Dog) myDog).displayClassName(); // Output: Class name: Dog
}
}
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
editionthat 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.
// Assuming Book class is defined as follows
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 methods
public String getTitle() {
return title;
}
public double getPrice() {
return price;
}
// Method to get book information
public String getBookInfo() {
return "Title: " + title + ", Price: $" + price;
}
@Override
public String toString() {
return getBookInfo();
}
}
// Textbook class extending Book
public class Textbook extends Book {
private int edition; // Private integer field for edition
// Constructor for the Textbook class
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; // Initialize the 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) {
return this.getTitle().equals(other.getTitle()) && this.edition >= other.edition;
}
// Overriding getBookInfo method
@Override
public String getBookInfo() {
return super.getBookInfo() + ", Edition: " + edition;
}
// Overriding toString method for convenient output
@Override
public String toString() {
return getBookInfo();
}
// Example of a main method to demonstrate functionality
public static void main(String[] args) {
Textbook t1 = new Textbook("Java Programming", 59.99, 2);
Textbook t2 = new Textbook("Java Programming", 49.99, 1);
Textbook t3 = new Textbook("Data Structures", 39.99, 1);
System.out.println(t1); // Displays textbook information
System.out.println(t2); // Displays textbook information
System.out.println(t3); // Displays textbook information
// Checking substitution
System.out.println("Can t1 substitute for t2? " + t1.canSubstituteFor(t2)); // Should return true
System.out.println("Can t1 substitute for t3? " + t1.canSubstituteFor(t3)); // Should return false
}
}
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());
| private String toString(){
| return "Shape length:"+ (new Double(this.length)).toString() + " width:" + (new Double(this.width)).toString();
| }
toString() in Shape cannot override toString() in java.lang.Object
attempting to assign weaker access privileges; was public
| return "Shape length:"+ (new Double(this.length)).toString() + " width:" + (new Double(this.width)).toString();
Double(double) in java.lang.Double has been deprecated and marked for removal
| return "Shape length:"+ (new Double(this.length)).toString() + " width:" + (new Double(this.width)).toString();
Double(double) in java.lang.Double has been deprecated and marked for removal
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 is correct
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()