12 - Inheritance: Abstract, Final, and Object Class

Abstract Classes

  • Animal Class Example:

    • An Animal class with a talk() method that prints generic animal sounds is awkward because it lacks specific implementations for different animals.
    • It's desirable to have a talk() method without a default implementation.
  • Abstract Keyword:

    • The abstract keyword can be used to define abstract classes and methods.
  • AbstractAnimal Example:

    abstract class Animal {
        public abstract void talk();
    }
    
    class Goose extends Animal {
        @Override
        public void talk() {
            System.out.println("Honk!");
        }
    }
    
    public class AbstractAnimal {
        public static void main(String[] args) {
            Animal a = new Goose();
            a.talk();
        }
    }
    
    • The talk() method is declared as abstract in the Animal class.
    • Any class containing an abstract method must also be declared as abstract.
  • Rules for Abstract Classes and Methods:

    • Any class can be declared abstract.
    • An abstract class cannot be instantiated directly (e.g., new Animal() will result in an error).
    • Any class with an abstract method must be declared as abstract.
    • Abstract methods do not require a body.
    • Abstract methods must be overridden by subclasses unless the subclass is also abstract.
  • AbstractError Example:

    abstract class Animal {
        public abstract void talk();
    }
    
    class Goose extends Animal {
        // Error: Did not override abstract method talk()
    }
    
    public class AbstractError {
        public static void main(String[] args) {
            Animal a = new Animal(); // Error: Cannot instantiate the type Animal
            a.talk();
        }
    }
    
    • Illustrates errors that occur when an abstract method is not overridden or when an abstract class is instantiated.
  • Expression Example:

    • The Expression class from a previous lecture is a good example of when to use abstract classes and methods.
    • It makes sense for Value, Add, and Multiply classes to implement specific methods, but the implementation is not well defined for the Expression class itself.

Final Keyword

  • Opposite of Abstract:

    • The final keyword is used to prevent overriding or inheritance.
  • final Methods:

    • final methods cannot be overridden in subclasses.
  • final Classes:

    • final classes cannot be inherited from.
  • Important Note:

    • A class with a final method does not necessarily need to be a final class.
  • Example with numLegs() Method:

    abstract class Animal {
        public abstract int numLegs();
    }
    
    class Spider extends Animal {
        public final int numLegs() {
            // All spiders have 8 legs
            return 8;
        }
    }
    
    class SpiderWith6Legs extends Spider {
        // Error: Cannot override the final method from Spider
        // public int numLegs() {
        //  return 6;
        // }
    }
    
    • The numLegs() method in the Spider class is declared as final, preventing subclasses from overriding it.
  • Example with Insect Class:

    final class Insect extends Animal {
        public int numLegs() {
            // All insects have 6 legs
            return 6;
        }
    }
    
    // class InsectWith8Legs extends Insect {
    //  // Error: Cannot inherit from final Insect
    // }
    
    • The Insect class is declared as final, preventing any class from inheriting from it.
    • This is useful to prevent incorrect inheritance.
  • final Variables:

    • The final keyword can also be used for variables and fields.
    • A final variable can only be assigned a value once; its value is considered constant.
    public class FinalVariable {
        public static void main(String[] args) {
            final int x = 5;
            // x = 6; // Error: Cannot assign a value to final variable  'x'
        final double y;
        y = 10.5;
        // y = 1.1; // Error: Cannot assign a value to final variable  'y'
    
        System.out.println(x);
        System.out.println(y);
    }
    
    }
  • Combining final with static:

    • final is often combined with static to create constants.
    public class CircleTools {
        public static final double PI = 3.14159;
    public static double area(double radius) {
        return PI * radius * radius;
    }
    
    public static double circumference(double radius) {
        return 2 * PI * radius;
    }
    
    public static void main(String[] args) {
        System.out.println("Area of a circle with radius 5:  " + area(5));
        System.out.println("Circumference of a circle with radius 5:  " + circumference(5));
    }
    
    }
    • Example: Defining PI as a static final variable ensures that its value cannot be redefined.
    • The formula for calculating area of circle is Area = PI * radius * radius and for circumference is Circumference = 2 * PI * radius

The Object Class

  • Built-in Class:

    • Java has a built-in class called Object.
  • Inheritance:

    • Every class in Java inherits from the Object class (except Object itself).
    • This means every class is part of the same inheritance tree.
  • Methods in Object:

    • The Object class has several methods that are inherited by all classes.
    • You can override these methods to change their behavior.
  • Focus on toString() and equals():

    • The lecture focuses on the toString() and equals() methods.

The toString() Method

  • Purpose:

    • The .toString() method converts objects to a string representation.
  • Inheritance from Object:

    • Because toString() is in the Object class, all objects can be represented as a string.
  • Usage:

    • When you concatenate a string with an object (`