9.1 What is inheritance?
- Inheritance is like a family, except the kids only have one parent instead of two
- For example:
The java code for it:
class Mom{
// CODE
}
class Son extends Mom{
// CODE
}
class Daughter extends Mom{
// CODE
}
In this example, the Son and Daughter inherits the Mom, meaning it inherit the components of the mother. This makes the “Son” and “Daughter” classes the subclass of the “Mom” class as they inherit the “Mom” class components and the “Mom” class the super class.
9.2 Using the Super keyword for Constructors
- One keyword to know is the super keyword
- The super keyword allows the subclass to store key variables in the main class’s contrstructer (also known as the super class)
- Example below:
public class Vehicle { //This is the Superclass, it inherits the key variables for its subclasses
public String Name; //They don't have to be public, but I just put public word for fun
public String Sound;
public int creation;
public int Mph;
public Vehicle(String name, int dateMade, String sound, int mph){ //Similar to the constructor used in Javascript. It maintains values within this superclass
Name = name;
Sound = sound;
creation = dateMade;
Mph = mph;
}
}
public class Car extends Vehicle {
public int capacity;
public Car(String name, int dateMade, String sound, int mph, int passangerCapacity){
super(name, dateMade,sound, mph); //Uses the superclass's constructor to store the key variables for the Car subclass
capacity = passangerCapacity;
}
public void Information(){ //Prints out the values the super class's constructors inherits
System.out.println(super.Sound + " " + this.Sound);
System.out.println("I am the " + super.Name);
System.out.println("I was made in " + super.creation);
System.out.println("I move at " + super.Mph +" miles per hour");
System.out.println("I hold at most " + capacity + " people");
}
}
public class Test {
public static void main(String[] args){
Car Tesla = new Car("Tesla", 2003, "VROOM!", 200, 5);
Tesla.Information();
}
}
Test.main(null);
VROOM! VROOM!
I am the Tesla
I was made in 2003
I move at 200 miles per hour
I hold at most 5 people
Popcorn Hack:
Make it so that a new instance of Bob runs
public class Worker {
String name;
int age;
double salary;
public Worker(String name, int age, double salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
}
public class Bob extends Worker {
String position;
public Bob(String name, int age, double salary, String position) {
super(name, age, salary);
this.position = position;
}
public void Information() {
System.out.println("My name is " + name);
System.out.println("I am " + age + " years old");
System.out.println("I make " + salary + " dollars a year");
System.out.println("I am a " + position);
}
}
public class Test {
public static void main(String[] args) {
Bob bob = new Bob("Bob", 20, 100000, "Software Engineer");
bob.Information(); // Call the Information method to display Bob's information
}
}
Test.main(null);
My name is Bob
I am 20 years old
I make 100000.0 dollars a year
I am a Software Engineer
9.3 Overriding Methods
Method overriding is a concept in object-oriented programming (OOP) that allows a subclass to provide a specific implementation of a method that is already defined in its superclass. This enables a subclass to provide its own behavior for a method while maintaining a relationship with its superclass.
In the context Java, here’s how method overriding works:
Inheritance: Method overriding is closely related to inheritance. You have a superclass (or base class) and a subclass (or derived class). The subclass inherits properties and behaviors from the superclass.
Superclass Method: The superclass defines a method. This method can be overridden by the subclass.
Subclass Overrides: In the subclass, you can provide a new implementation of the same method. This is done by using the same method name, return type, and parameter list.
@Override Annotation (Java): In Java, it’s common to use the @Override annotation to explicitly indicate that a method in the subclass is intended to override a method in the superclass. This helps catch errors during compilation if the method doesn’t correctly match the superclass method signature.
Why Override Methods:
Method overriding is used for several reasons:
Customization: It allows you to customize or extend the behavior of a superclass method in the subclass to meet the specific needs of the subclass.
Polymorphism: Method overriding is a key component of polymorphism. It enables you to treat objects of the subclass as if they were objects of the superclass, promoting flexibility and extensibility.
Consistency: Method overriding helps maintain a consistent interface for classes in an inheritance hierarchy. Code that uses the superclass doesn’t need to be changed when a subclass overrides a method.
Code Reusability: It promotes code reusability by allowing you to build on existing code in the superclass.
class Animal {
void makeSound() {
System.out.println("Animals make sounds");
}
}
class Dog extends Animal {
@Override
void makeSound() {
System.out.println("Dog barks");
}
}
class Cat extends Animal {
@Override
void makeSound() {
System.out.println("Cat meows");
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Animal();
Animal dog = new Dog();
Animal cat = new Cat();
animal.makeSound(); // Output: Animals make sounds
dog.makeSound(); // Output: Dog barks
cat.makeSound(); // Output: Cat meows
}
}
Main.main(null);
Animals make sounds
Dog barks
Cat meows
In this example:
We have a base class Animal with a method makeSound().
We create two subclasses, Dog and Cat, which inherit from the Animal class.
Both Dog and Cat classes override the makeSound() method with their own implementations.
In the main method, we create instances of the base class and its subclasses.
We demonstrate polymorphism by calling the makeSound() method on objects of the base class and the subclasses. The method called depends on the actual type of the object, not the reference type.
This showcases how method overriding allows you to provide specific implementations for methods in subclasses, promoting polymorphism and custom behavior for each subclass.
Another Example:
<img class=”image” src=”https://github.com/AniCricKet/musical-guacamole/assets/91163802/576237f9-cdc4-409b-84f9-96dffe0cdd5c” width=32%> <img class=”image” src=”https://github.com/AniCricKet/musical-guacamole/assets/91163802/03923e22-2b6e-4e4d-9244-1d5145f6c6d9” width=32%> <img class=”image” src=”https://github.com/AniCricKet/musical-guacamole/assets/91163802/5fe0c72c-c17b-4edb-a567-8c9098998aac” width=32%>
Imagine you’re building a program to manage sports team rosters. You can have a base class ‘Athlete’ representing common attributes and actions of all athletes. Then, create subclasses for specific sports like ‘FootballPlayer’, ‘BasketballPlayer’, and ‘SoccerPlayer’.
// Base Class
class Athlete {
String name;
int age;
int jerseyNumber;
String position;
public Athlete(String name, int age, int jerseyNumber, String position) {
this.name = name;
this.age = age;
this.jerseyNumber = jerseyNumber;
this.position = position;
}
public void train() {
System.out.println(name + " is training.");
}
public void displayInfo() {
System.out.println("Athlete Info:");
System.out.println("Name: " + name);
System.out.println("Age: " + age);
System.out.println("Jersey Number: " + jerseyNumber);
System.out.println("Position: " + position);
}
}
Athlete athlete = new Athlete("John Mortensen", 19, 4, "Teacher");
athlete.train();
athlete.displayInfo();
John Mortensen is training.
Athlete Info:
Name: John Mortensen
Age: 19
Jersey Number: 4
Position: Teacher
class FootballPlayer extends Athlete {
public FootballPlayer(String name, int age, int jerseyNumber, String position) {
super(name, age, jerseyNumber, position);
}
@Override
public void train() {
System.out.println(name + " is practicing football drills.");
}
@Override
public void displayInfo() {
super.displayInfo();
}
}
class BasketballPlayer extends Athlete {
public BasketballPlayer(String name, int age, int jerseyNumber, String position) {
super(name, age, jerseyNumber, position);
}
@Override
public void train() {
System.out.println(name + " is shooting 3s on the court.");
}
@Override
public void displayInfo() {
super.displayInfo();
}
}
class SoccerPlayer extends Athlete {
public SoccerPlayer(String name, int age, int jerseyNumber, String position) {
super(name, age, jerseyNumber, position);
}
@Override
public void train() {
System.out.println(name + " is practicing taking free kicks.");
}
@Override
public void displayInfo() {
super.displayInfo();
}
}
FootballPlayer footballPlayer = new FootballPlayer("Tyreek Hill", 28, 10, "Wide Receiver");
BasketballPlayer basketballPlayer = new BasketballPlayer("Jimmy Butler", 32, 22, "Small Forward");
SoccerPlayer soccerPlayer = new SoccerPlayer("Neymar Jr", 31, 10, "Left Winger");
footballPlayer.train();
footballPlayer.displayInfo();
System.out.println();
basketballPlayer.train();
basketballPlayer.displayInfo();
System.out.println();
soccerPlayer.train();
soccerPlayer.displayInfo();
System.out.println();
Tyreek Hill is practicing football drills.
Athlete Info:
Name: Tyreek Hill
Age: 28
Jersey Number: 10
Position: Wide Receiver
Jimmy Butler is shooting 3s on the court.
Athlete Info:
Name: Jimmy Butler
Age: 32
Jersey Number: 22
Position: Small Forward
Neymar Jr is practicing taking free kicks.
Athlete Info:
Name: Neymar Jr
Age: 31
Jersey Number: 10
Position: Left Winger
Explanation:
In this Java code, you have a basic “Athlete” class with information and actions that all athletes share. Then, there are specific types of athletes (football, basketball, and soccer players) that inherit these common traits but also have their unique behaviors, like training routines. Method overriding lets them have their own way of training while keeping the shared information, making the code easy to manage and reuse for different types of athletes.
Popcorn Hack:
Why is it helpful to have a common base class like ‘Athlete’ for all these different types of athletes? How does it make the code more organized?
Because there are a coiuple things to generalize about athletes. You can assume they play a sport, are on a team, and have a salary
9.4 Using Super keyword for Methods
- Why only use super for constructors when you can use them for methods too?
- With the super key word, not only can you store variables, but also store methods
class Animal{
public void Introduction(String name){
System.out.println("I am a " + name);
}
}
class Dog extends Animal{
public void Woof(){
super.Introduction("Dog");//Inherits the introduction method in the Animal Class, then introduces itself as a dog
System.out.println("Woof!"); //Does its own thing
}
}
class Cow extends Animal{
public void Moo(){
super.Introduction("Cow");//Inherits the introduction method in the Animal Class, then introduces itself as a cow
System.out.println("MOOOO!");//Does its own thing
}
}
class Test{
public static void main(String[] args){
Dog dog = new Dog();
Cow cow = new Cow();
dog.Woof();
cow.Moo();
}
}
Test.main(null);
I am a Dog
Woof!
I am a Cow
MOOOO!
9.4 Hack
Finish up the code with this criteria: All subclasses must say their origin, the origin can be from SchoolSupply class, and it must run through main.
class SchoolSupply {
String Name;
String Color;
public SchoolSupply(String Name, String Color) {
this.Name = Name;
this.Color = Color;
}
}
class Pencil extends SchoolSupply {
public Pencil(String Name, String Color) {
super(Name, Color);
}
public void Information() {
System.out.println("I am a " + Name);
System.out.println("My color is " + Color);
}
}
class Eraser extends SchoolSupply {
public Eraser(String Name, String Color) {
super(Name, Color);
}
public void Information() {
System.out.println("I am an " + Name);
System.out.println("My color is " + Color);
}
}
public class Test {
public static void main(String[] args) {
Pencil pencil = new Pencil("Pencil", "Yellow");
Eraser eraser = new Eraser("Eraser", "Pink");
pencil.Information();
System.out.println();
eraser.Information();
}
}
Test.main(null);
I am a Pencil
My color is Yellow
I am an Eraser
My color is Pink
9.5 Creating References Using Inheritance Hierarchies
Inheritance can be thought as an upside down tree with the branches on the top and the tree on the bottom. The branches is the superclass while the tree are the subclasses of this superclass. A visual representation of this tree is called a type diagram or hierarchy tree.
A sample structure would be like:
public class A
public class B extends A
public class C extends B
public class D extends C
public class E extends I
public class F extends I
public class G extends H
public class H extends A
public class I extends H
Popcorn Hack
- Draw a hierarchy tree for the above structure and add the picture here
This structure works as C not only inherits properties from B, but it also inherits properties from A. B is like C’s parent and A is like C’s grandparent. Think about it like real life, all families inherit something from their ancestors.
In addition, you could also create objects like this:
C c = new C();
B c = new C();
A c = new C();
// This is the above example in code form
class A {}
class B extends A {}
class C extends B {}
public class Main {
public static void main(String[] args) {
C c = new C(); // variable c is of type C
B b = new C(); // variable b is of type B, but refers to an instance of C
A a = new C(); // variable a is of type A, but refers to an instance of C
}
}
9.6 Polymorphism
A reference variable is polymorphic when it can refer to objects from different classes at different points in time.
A method or operator is considered polymorphic when it is overridden in at least one subclass.
Polymorphism is the act of executing an overridden non-abstract method from the correct class at runtime based on the actual object type.
Polymorphism allows the method for a method call to be executed based on the class of the object referenced instead of the declared class.
Example 1
Java polymorphism is mainly split into 2 types
Runtime
- Process in which a function call to the overridden method is resolved at Runtime. This type of polymorphism is achieved by Method Overriding.
Compile
- Also known as static polymorphism. This type is achieved by function overloading or operater overloading
- Note: But java doesnt support Operator Overloading
- When there are multiple functions with the same name but different parameters then these functions are said to be overloaded. Functions can be overloaded by changes in the number of arguments or/and a change in the type of arguments.
Here is an example of compile polymorphism
// Class 1
// Helper class
class Helper {
// Method 1
// Multiplication of 2 numbers
static int Multiply(int a, int b)
{
// Return product
return a * b;
}
// Method 2
// // Multiplication of 3 numbers
static int Multiply(int a, int b, int c)
{
// Return product
return a * b * c;
}
}
// Class 2
// Main class
class GFG {
// Main driver method
public static void main(String[] args)
{
// Calling method by passing
// input as in arguments
System.out.println(Helper.Multiply(2, 4));
System.out.println(Helper.Multiply(2, 7, 3));
}
}
GFG.main(null)
8
42
Example 2 & Popcorn Hack
Before executing cell, look at the example below. Think about which methods compiles? Which methods execute?
import java.util.Random;
public class Entertainer{
private String talent;
public Entertainer (String t){
talent = t;
}
public String getTalent(){
return talent;
}
}
public class Comedian extends Entertainer{
private ArrayList<String> jokes;
public Comedian(String t, ArrayList<String> jks){
super(t);
jokes = jks;
}
public String getTalent(){
return "Comedy style: " + super.getTalent();
}
public String tellJoke(){
return jokes.get((int)(Math.random()*jokes.size()));
}
}
// Which one of these will run? Which one of these will not? Comment out the line that isn't working and explain why
Entertainer kevin = new Entertainer("Musician");
System.out.println(kevin.getTalent());
System.out.println(kevin.tellJoke());
ArrayList<String> oneLiners = new ArrayList<String>();
//Add code which adds jokes to oneLiners like... Why did the programmer quit his job?.. Why did the developer go broke?..
Entertainer soham = new Comedian(“satire”, oneliners);
System.out.println(soham.getTalent());
System.out.println(((Comedian)soham).tellJoke());
Musician
| System.out.println(kevin.tellJoke());
cannot find symbol
symbol: method tellJoke()
Example 3
Here is an example of runtime polymorphism
// Class 1
// Helper class
class Parent {
// Method of parent class
void Print()
{
// Print statement
System.out.println("parent class");
}
}
// Class 2
// Helper class
class subclass1 extends Parent {
// Method
void Print() { System.out.println("subclass1"); }
}
// Class 3
// Helper class
class subclass2 extends Parent {
// Method
void Print()
{
// Print statement
System.out.println("subclass2");
}
}
// Class 4
// Main class
class GFG {
// Main driver method
public static void main(String[] args)
{
// Creating object of class 1
Parent a;
// Now we will be calling print methods
// inside main() method
a = new subclass1();
a.Print();
a = new subclass2();
a.Print();
}
}
GFG.main(null)
subclass1
subclass2
9.7 Object Superclass
Now that we have learned about inheritance, what even allows our classes and objects that we have created to work the way they do? Where do the general characteristics of all objects come from? The answer lies in the super class.
The Object class is the superclass of all other classes as well as arrays and other data types. The Object class is part of the java.lang package.
When we call a constructor to a “top-level class” that the coder hasn’t declared a superclass for, the Object constructor is implicitly called. In other words, the Object constructor is implicitly called when we call a constructor in a class that doesn’t explicitly extend another class. This will give the object some properties and methods that are common to all classes.
Example 1
public class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public static void main(String[] args) {
Person person1 = new Person("Jane Doe", 30);
Person person2 = new Person("Jane Doe", 30);
System.out.println(person1.equals(person1)); // Since person1 and person1 are the same object, the equals() method will return true
System.out.println(person1.equals(person2)); // Since person1 and person2 are different objects, the equals() method will return false even though they have the same contents
}
}
Person.main(null);
// The equals() method is inherited from the Object class
// By default, the equals() method in the Object class checks for object identity, which means it compares memory addresses to see if two references point to the exact same object
// In the code, person1 and person2 are distinct objects, so they have different memory addresses
// When we call person1.equals(person2), it checks if the memory addresses are the same (which they are not), so it returns false.
true
false
Example 2
public class Book {
String title;
String author;
public Book(String title, String author) {
this.title = title;
this.author = author;
}
public static void main(String[] args) {
Book book = new Book("The Catcher in the Rye", "J.D. Salinger");
int hashCode = book.hashCode();
System.out.println("HashCode: " + hashCode); // The output will be a unique integer value representing the object's hash code. The integer value will be different every time you run it
}
}
Book.main(null);
// The hashCode() method in the Object class returns a unique integer value for each object
// This value is typically based on the object's memory address.
// In the code, when we call book.hashCode(), it generates a unique integer value representing the book object
// This value can be useful for various purposes, such as organizing objects in collections like HashMaps or HashSet where it helps in efficient retrieval of objects.
HashCode: 2094718255
Hacks
- Popcorn Hacks (0.2): Participate in the discussion and fill in all of the blanks.
- MC Questions (0.1): Answer the 10 MC questions below with short explanations
- FRQ Hacks (0.5): Make a complex FRQ that involves everything we taught. Be sure to have a sample solution along with scoring guidlines and how the solution is scored.
- Challenge (0.1): Make an example that uses everything we taught and it must run through main and uses input and output. Points will be awarded for complexity and creativity
MC Questions
-
The reference variable car of type Vehicle will successfully have the Car object allocated to it. This occurs because a subclass may be the parent class.
-
B c = new C(); The subclass is unable to produce a Parent class instance.
-
Class G items can be regarded as Class H and Class J objects. Class G receives all of Class H’s attributes, and since H receives J, G also receives J.
-
A subclass of B is C. D is the grandfather of C since B is D’s parent and D is C’s parent.
-
A compile-time error will occur. Subclass is unable to create a parent class instance.
-
The lines “J j = new J();,” “J k = new K();,” and “J l = new L();” Each response is valid.
-
Because the subclass inherits, there is inheritance
-
H is a subclass of G that is not direct. G is superseded by B, and H by B.
-
The subclasses are the branches, and the superclass is the root. the root gives rise to the subclass
-
The parent can descend after the item is correctly assigned to the reference variable.
HACK FRQ
Create a class hierarchy for animals, with a superclass called Animal and subclasses for Dog, Cat, and Bird. The Animal class should have the following methods:
speak(): This method should print a message to the console indicating the animal’s sound. For example, a Dog object would print “Woof!” when the speak() method is called. eat(): This method should print a message to the console indicating what the animal eats. For example, a Cat object would print “I eat mice” when the eat() method is called. The Dog, Cat, and Bird subclasses should override the speak() and eat() methods to provide specific implementations for each animal.
public class Animal {
public void speak() {
System.out.println("I am an animal.");
}
public void eat() {
System.out.println("I eat food.");
}
}
public class Dog extends Animal {
@Override
public void speak() {
System.out.println("Woof!");
}
@Override
public void eat() {
System.out.println("I eat dog food.");
}
}
public class Cat extends Animal {
@Override
public void speak() {
System.out.println("Meow!");
}
@Override
public void eat() {
System.out.println("I eat mice.");
}
}
public class Bird extends Animal {
@Override
public void speak() {
System.out.println("Chirp!");
}
@Override
public void eat() {
System.out.println("I eat seeds.");
}
}
Animal animal = new Dog();
animal.speak(); // Prints "Woof!"
animal.eat(); // Prints "I eat dog food."
animal = new Cat();
animal.speak(); // Prints "Meow!"
animal.eat(); // Prints "I eat mice."
animal = new Bird();
animal.speak(); // Prints "Chirp!"
animal.eat(); // Prints "I eat seeds."
Woof!
I eat dog food.
Meow!
I eat mice.
Chirp!
I eat seeds.
Explanation of subtopics
Inheritance: The Dog, Cat, and Bird classes inherit from the Animal class. This means that they have access to all of the methods and variables that are defined in the Animal class. Super (constructors and methods): The super() keyword can be used to call the superclass constructor or methods. For example, the Dog() constructor calls the Animal() constructor to ensure that the Animal class’s instance variables are properly initialized. Overriding methods: The Dog, Cat, and Bird classes override the speak() and eat() methods from the Animal class. This means that they provide their own implementations for these methods. Polymorphism: Polymorphism is the ability of objects of different classes to respond to the same method call in different ways. For example, the speak() method can be called on objects of the Dog, Cat, and Bird classes, and each object will respond with its own unique sound. Object superclass: All Java classes inherit from the Object class. This class provides a number of useful methods, such as equals(), hashCode(), and toString().