Review on Inheritance

Basics of Inheritance:

  • Superclass and Subclass: In Java, a superclass (or base class) is the class being inherited from, while a subclass (or derived class) is the class that inherits from the superclass.
  • extends Keyword: To create a subclass, you use the extends keyword followed by the name of the superclass.
class SuperClass {
    //methods and fields
}

class SubClass extends SuperClass {  
    //methods and fields  
}  

Basics Continued

Code:

class SuperClass {
    // Fields
    private int superField;
    
    // Constructor
    public SuperClass(int superField) {
        this.superField = superField;
    }
    
    // Method
    public void superMethod() {
        System.out.println("SuperClass Method");
    }
}

// SubClass
class SubClass extends SuperClass {
    // Fields
    private int subField;
    
    // Constructor
    public SubClass(int superField, int subField) {
        // Call superclass constructor using 'super' keyword
        super(superField);
        this.subField = subField;
    }
    
    // Method
    public void subMethod() {
        System.out.println("SubClass Method");
    }
}

Explanation:

SuperClass:

  • SuperClass is the base class.
  • It has a private superField, a constructor SuperClass(int superField) that initializes superField, and a method superMethod().

SubClass:

  • SubClass is a subclass of SuperClass.
  • It extends SuperClass using the extends keyword.
  • It adds its own field subField, a constructor SubClass(int superField, int subField) that initializes both superField (from the superclass) and subField, and a method subMethod().

NOTE Subclass instance can access both superfield and subfield and subMethod and superMethod while the Superclass instance can only access superclass attributes

2022 FRQ Question 2

The Book class is used to store information about a book. A partial Book class definition is shown.

public class Book { 

    private String title; // The title of the book
    private double price; // The price of the book

    public Book(String bookTitle, double bookPrice) { // Creates a new Book with given title and price
        // implementation not shown
    } 

    public String getTitle() { // Returns the title of the book
        return title; 
    } 
    
    public String getBookInfo() { // Returns a string containing the title and price of the Book
        return title + "-" + price; 
    } 

    // There may be instance variables, constructors, and methods that are not shown.
} 

You will write a class Textbook, which is a subclass of Book. A Textbook has an edition number, which is a positive integer used to identify different versions of the book. The getBookInfo method, when called on a Textbook, returns a string that also includes the edition information, as shown in the example.

Information about the book title and price must be maintained in the Book class. Information about the edition must be maintained in the Textbook class. The Textbook class contains an additional method, canSubstituteFor, which returns true if a Textbook is a valid substitute for another Textbook and returns false otherwise. The current Textbook is a valid substitute for the Textbook referenced by the parameter of the canSubstituteFor method if the two Textbook objects have the same title and if the edition of the current Textbook is greater than or equal to the edition of the parameter.

Point distribution

  • Correctly make Class with header and extends - 2
  • Calls super as the first line with the appropriate parameters - 1
  • Declares appropriate private instance variable - 1
  • Declares at least one required method and all declared headers are correct - 1
  • getEdition returns value of instance variable - 1
  • canSubstituteFor determines whether true or false should be returned based on - 1 comparison of book titles and editions - 1
  • getBookInfo calls super.getBookInfo - 1
  • Constructs information string - 1

Points to be careful on

  • Point for declaring class correctly (always a point), Point for declaring a constructor, use meaningful variable names, ensure constructor is in correct order, constructor must call super as first line.
  • Title and Price must be maintained in the book class according to problem, so super has to be used. Edition is specific to textbook, means it should be private and has to be correctly initialized outside of the constructor. Sometimes you get point if you correctly write at least one correct method header.
  • Make sure you understand super! Super is a way to access the parent classes variables without using a constructor.

Example solution

public class Textbook extends Book
{
    //private instance variable for edition number
    private int edition;

    //full constructor must have String, double, int matches the examples given in table
    public Textbook(String t, double p, int e)
    {
        //must be first: let Book manage its variables
        super(t, p);

        //instance variable on left, parameter on right
        this.edition = e;
    }

    //basic accessor method, note: only need getEdition because Book manages the others
    public int getEdition()
    {
        return edition;
    }

    //similar to toString: all data returned nicely formatted
    public String getBookInfo()
    {
        //need to use the method from Book to get the title and price, then concatenate the edition
        return super.getBookInfo() + "-" + edition;
    }

    //similar to equals: compare the two data pieces
    public boolean canSubstituteFor(Textbook other)
    {
        //titles are equals and edition is >= to other's
        return this.getTitle().equals(other.getTitle()) &&
        this.getEdition() >= other.getEdition();
    }
}

Key Components

  1. Class Header:
    • public class Textbook extends Book: Declares the class header.
  2. Instance Variable:
    • private int edition: This variable stores the edition number of the textbook.
  3. Constructor:
    • public Textbook(String t, double p, int e): This is the constructor method for the Textbook class. It takes three parameters: a String for the title (t), a double for the price (p), and an int for the edition (e)
    • It initializes the instance variables of the Textbook class using the super keyword to call the constructor of the superclass Book, passing the title and price
    • Then, it sets the edition instance variable to the value provided.
  4. Accessor Method:
    • public int getEdition(): This method returns the edition number of the textbook.
  5. Other Methods:
    • public String getBookInfo(): This method returns a string containing information about the textbook
    • It calls the getBookInfo() method of the superclass Book to get the title and price, and then appends the edition number to it.
    • public boolean canSubstituteFor(Textbook other): This method checks if the current textbook can substitute another textbook (other)
      • It returns true if the titles are equal and if the edition of the current textbook is greater than or equal to the edition of the other textbook.

** It is not required to include a main method in the code as not required by scoring guidelines **

Common Mistakes

  1. Misunderstanding the Problem
    • Not fully understanding the requirements given in the problem can lead to incorrect solutions or implementations.
  2. Incomplete Implementation
    • Forgetting to implement all necessary methods or attributes as per the requirements of the problem.
  3. Misinterpreting Method Overrides
    • Incorrectly overriding methods from the superclass in subclasses, leading to unexpected behavior.
  4. Ignoring Access Modifiers
    • Forgetting to consider access modifiers like public, private, and protected, which can result in errors in accessing or modifying variables and methods.
    • public: Accessible by any class.
    • protected: Accessible within the same package and subclasses.
    • private: Not inherited, not accessible.
    • default (no modifier): Accessible within the same package.
  5. Failure to Call Superclass Constructors
    • Forgetting to call constructors of the superclass in subclasses can lead to uninitialized variables or incorrect behavior of inherited attributes.

Hacks

Inheritance (Unit 9)

Situation: You are developing a program to manage a zoo, where various types of animals are kept in different enclosures. To streamline your code, you decide to use inheritance to model the relationships between different types of animals and their behaviors.

(a) Explain the concept of inheritance in Java. Provide an example scenario where inheritance is useful.

Inheritence allows POJOs to be able to use attributes and methods from other classes that act as parents to them. This can also allow a more specific instance of a broader class to be used (in the examples above, a Textbook being a more specific type of Book (parent class)). Inheritence is implemented by declaring a class a subclass of another class (the parent class), which will allow the class to utilize attributes, methods, and (using super()) constructors of the parent.

Inheritance can be useful, for example, if creating a subset of a broader object type that must handle a greater number of variables/attributes and/or must handle parent variables/attributes in a way that is different from the parent. If a Sandwich class, for example, was being used, a Hamburger subclass may be helpful if we wanted more specific data about the hamburger. It might have a meatWeight attribute or a cookType (medium, well, etc.) attribute that a sandwich would not need/have. However, by extending Sandwich, the hamburger class could utilize the attribtes like the contents of the Sandwich that apply to the burger, too.

(b) Code:

You need to implement a Java class hierarchy to represent different types of animals in the zoo. Create a superclass Animal with basic attributes and methods common to all animals, and at least three subclasses representing specific types of animals with additional attributes and methods. Include comments to explain your code, specifically how inheritance is used.

// here is the basic base Animal class
public class Animal {
    // these attributes can apply to any animal that exists
    private String speciesName;
    private int massInKg;
    private int speedInKmph;
    private String[] habitats;
    // this constructor will be inherited by the subclasses
    public Animal(String speciesName, int massInKg, int speedInKmph, String[] habitats) {
        this.speciesName = speciesName;
        this.massInKg = massInKg;
        this.speedInKmph = speedInKmph;
        this.habitats = habitats;
    }
    // getter for other classes
    public String getSpeciesName() {
        return this.speciesName;
    }
    // all animals move at some speed, so this method works for all animals
    public void move() {
        System.out.println("The " + this.speciesName + " moves at " + this.speedInKmph + " kilometers per hour!");
    }
}

public class PetDog extends Animal {
    // an additional attribute for all types of dogs
    private String owner;
    // this constructor will use super() to implement the Animal() constructor
    public PetDog(String speciesName, int massInKg, int speedInKmph, String[] habitats, String owner) {
        // calling super(), which calls the parent class's constructor
        super(speciesName, massInKg, speedInKmph, habitats);
        // setting new attribute separate from super
        this.owner = owner;
    }
    // getter for testing
    public String getOwner() {
        return this.owner;
    }
    // new method: bark (because dogs bark idk bro)
    public void bark() {
        System.out.println("The " + this.getSpeciesName() + " barks! The owner, " + this.owner + ", tells his/her dog to stop!");
    }
}

public class Fish extends Animal {
    // an additional attribute for all types of fish
    private int numberOfEggs;
    // this constructor will use super() to implement the Animal() constructor
    public Fish(String speciesName, int massInKg, int speedInKmph, String[] habitats, int numberOfEggs) {
        // calling super(), which calls the parent class's constructor
        super(speciesName, massInKg, speedInKmph, habitats);
        // setting new attribute separate from super
        this.numberOfEggs = numberOfEggs;
    }
    // getter for testing
    public int getNumberOfEggs() {
        return this.numberOfEggs;
    }
    // new method: lay eggs (because fish lay eggs)
    public void layEggs() {
        System.out.println("The " + this.getSpeciesName() + " lays about " + this.numberOfEggs + " eggs.");
    }
}

public class Lizard extends Animal {
    // an additional attribute for all types of lizards
    private boolean hasTail;
    // this constructor will use super() to implement the Animal() constructor
    public Lizard(String speciesName, int massInKg, int speedInKmph, String[] habitats, boolean hasTail) {
        // calling super(), which calls the parent class's constructor
        super(speciesName, massInKg, speedInKmph, habitats);
        // setting new attribute separate from super
        this.hasTail = hasTail;
    }
    // getter for testing
    public boolean getHasTail() {
        return this.hasTail;
    }
    // new method: remove tail (works together with the following method)
    public void removeTail() {
        if (this.hasTail) {
            this.hasTail = false; // the lizard no longer has a tail
            System.out.println("The lizard's tail was removed!");
        } else { // if the tail was already removed
            System.out.println("The lizard's tail has already been removed.");
        }
    }
    // new method: remove tail (lizards can regrow their tails)
    public void regrowTail() {
        if (!(this.hasTail)) {
            this.hasTail = true; // the tail grows back, so it has a tail now
            System.out.println("The lizard's tail regrew!");
        } else { // if it already has a tail
            System.out.println("The lizard already has a tail.");
        }
    }
}

public class Main {
    // main method used for functionality; see subclasses below
    public static void main(String[] args) {
        // creating a base animal object
        String[] animalHabitats = {"savannah", "marshland"};
        Animal animal = new Animal("creature", 100, 20, animalHabitats);
        animal.move(); // showing method functionality
        System.out.println();

        // instance of petdog subclass to show functionality
        String[] goldenRetrieverHabitats = {"suburban home"};
        PetDog goldenRetriever = new PetDog("Golden Retriever", 65, 30, goldenRetrieverHabitats, "John Goldenretriever");
        System.out.println("The owner's name is " + goldenRetriever.getOwner() + "."); // unique attribute to this subclass
        goldenRetriever.move(); // parent method is called
        goldenRetriever.bark(); // unique subclass method
        System.out.println();

        // instance fish subclass to show functionality
        String[] goldfishHabitats = {"freshwater lakes", "freshwater bogs", "freshwater swamps"};
        Fish goldfish = new Fish("goldfish", 1, 1, goldfishHabitats, 1000);
        goldfish.move(); // parent method is being called
        goldfish.layEggs(); // both a unique subclass method and one that shows a unique attribute
        System.out.println();

        // instance of lizard subclass to show functionality
        String[] blackIguanaHab = {"forests"};
        Lizard blackIg = new Lizard("black iguana", 5, 20, blackIguanaHab, true);
        blackIg.move(); // parent method is being called
        blackIg.removeTail(); // subclass method using new attribute hasTail
        blackIg.regrowTail(); // subclass method that uses that same attribute again with more logic
    }
}

Main.main(null);
The creature moves at 20 kilometers per hour!

The owner's name is John Goldenretriever.
The Golden Retriever moves at 30 kilometers per hour!
The Golden Retriever barks! The owner, John Goldenretriever, tells his/her dog to stop!

The goldfish moves at 1 kilometers per hour!
The goldfish lays about 1000 eggs.

The black iguana moves at 20 kilometers per hour!
The lizard's tail was removed!
The lizard's tail regrew!