Abstraction with Abstract Methods

Abstraction in Java is a key concept in object-oriented programming (OOP) that focuses on hiding the implementation details and showing only the essential features of an object. It allows you to work with objects at a higher level, focusing on what an object does rather than how it does it. Java provides abstraction primarily through abstract classes and interfaces.

Abstract Methods

An abstract method in Java is a method that does not have a body; instead, it’s a method declaration that must be implemented by any subclass that extends the abstract class. In other words, an abstract method represents behavior that the subclass is expected to define. Abstract methods are declared using the abstract keyword, and they exist only in abstract classes or interfaces.

Syntax of Abstract Methods

An abstract method has the following syntax:

abstract class ClassName {
    // Abstract method
    public abstract void methodName();
}

Here:

  • The method methodName() is declared as abstract and has no body.
  • The class ClassName must also be declared as abstract, because it contains an abstract method.

Characteristics of Abstract Methods

  1. No Method Body: Abstract methods do not provide an implementation. They are meant to be overridden by subclasses that extend the abstract class.
  2. Abstract Classes: Any class that contains at least one abstract method must be declared as abstract. An abstract class cannot be instantiated directly; it can only be subclassed.
  3. Subclass Responsibility: It is the responsibility of the subclass that extends the abstract class to provide implementations for all abstract methods in the parent class.

Example of Abstract Method

// Abstract class with an abstract method
abstract class Animal {
    // Abstract method (does not have a body)
    public abstract void sound();

    // Regular method with a body
    public void breathe() {
        System.out.println("Breathing...");
    }
}

// Subclass provides the implementation for the abstract method
class Dog extends Animal {
    @Override
    public void sound() {
        System.out.println("Bark");
    }
}

class Cat extends Animal {
    @Override
    public void sound() {
        System.out.println("Meow");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog();
        dog.sound();   // Output: Bark
        dog.breathe(); // Output: Breathing...

        Animal cat = new Cat();
        cat.sound();   // Output: Meow
        cat.breathe(); // Output: Breathing...
    }
}

Explanation of the Example

  1. Abstract Class Animal:
    • The class Animal is declared as abstract because it contains an abstract method sound().
    • The method breathe() is a concrete method, so it can be inherited and used by subclasses as is.
  2. Concrete Subclasses Dog and Cat:
    • The Dog and Cat classes extend the Animal abstract class and provide concrete implementations for the sound() method.
    • Both subclasses are required to implement the sound() method, as it is abstract in the parent class.
  3. Polymorphism in Action:
    • The dog and cat objects are instances of Dog and Cat, but they are referenced by the Animal type. This allows us to call sound() polymorphically, with each subclass providing its own behavior for the method.

Why Use Abstract Methods?

  1. Enforcing a Contract:
    • Abstract methods enforce a contract between the abstract class and its subclasses. The abstract class defines behaviors (in the form of abstract methods) that its subclasses must implement. This is useful for establishing a common interface for different subclasses.
  2. Promoting Reusability:
    • Abstract classes allow you to define common functionality that can be shared among different subclasses. For example, in the Animal class above, the breathe() method is shared by all animals, while each subclass is responsible for implementing its specific behavior through the sound() method.
  3. Flexible Design:
    • Abstract methods help achieve a flexible and extensible design. By defining an abstract class, you can create a blueprint that multiple subclasses can follow, ensuring consistency across related classes while still allowing each subclass to provide its unique behavior.
  4. Polymorphism:
    • Abstract classes and methods are central to achieving polymorphism in Java. By using abstract methods, you can create subclasses that each implement the abstract method differently, allowing for runtime method dispatch based on the object’s actual type.

Abstract Classes vs Interfaces

Both abstract classes and interfaces are tools for achieving abstraction in Java, but there are key differences:

  1. Abstract Classes:
    • Can contain both abstract methods (without body) and concrete methods (with implementation).
    • Can have state (instance variables), and those variables can be inherited by subclasses.
    • Allows you to define default behavior that all subclasses can inherit and use.
    • A class can extend only one abstract class (because Java supports single inheritance).
  2. Interfaces:
    • In older versions of Java, interfaces could contain only abstract methods, but from Java 8 onwards, they can also have default methods (methods with implementation).
    • Interfaces cannot hold state. All fields are implicitly public, static, and final (i.e., constants).
    • A class can implement multiple interfaces.
    • Interfaces are used to define a contract that multiple unrelated classes can implement.

When to Use Abstract Methods

  • Abstract methods are useful when you want to provide a base class that defines certain behaviors (methods) without implementing them. This allows subclasses to provide their own specific implementation for these behaviors.
  • Use abstract methods in situations where:
    • You have a base class that defines common properties and behaviors but leaves some method implementations to the subclasses.
    • You want to ensure that certain methods are implemented by every subclass, but the exact implementation differs across subclasses.

Rules for Abstract Methods and Classes

  1. Abstract Methods in Abstract Classes: Abstract methods can only be declared in abstract classes. If a class contains one or more abstract methods, the class itself must be declared abstract.
  2. Cannot Instantiate Abstract Classes: You cannot create an instance of an abstract class directly. For example, Animal animal = new Animal(); would result in a compilation error. You must create an instance of a subclass that provides concrete implementations of all abstract methods.
  3. Subclass Must Implement All Abstract Methods: Any subclass of an abstract class must implement all of its abstract methods, unless the subclass itself is declared abstract. For example, if Dog did not implement the sound() method, it would need to be declared abstract.
  4. Abstract Methods Cannot Be Private: Abstract methods must be either protected or public. They cannot be private because they are meant to be overridden by subclasses, and private methods are not accessible to subclasses.
  5. Static Methods Cannot Be Abstract: A method that is marked as static cannot also be marked as abstract because static methods belong to the class itself and are not meant to be overridden by subclasses.

Abstract Classes with Concrete Methods

An abstract class can have both abstract methods (without implementation) and concrete methods (with implementation). This allows you to define some default behavior in the abstract class while leaving certain behaviors to be defined by subclasses.

abstract class Vehicle {
    // Abstract method
    public abstract void start();

    // Concrete method
    public void stop() {
        System.out.println("Vehicle stopped");
    }
}

class Car extends Vehicle {
    @Override
    public void start() {
        System.out.println("Car started");
    }
}

public class Main {
    public static void main(String[] args) {
        Vehicle car = new Car();
        car.start();  // Output: Car started
        car.stop();   // Output: Vehicle stopped
    }
}

In this example:

  • The Vehicle class defines an abstract method start() and a concrete method stop().
  • The Car subclass provides the implementation for the abstract method start(), while it inherits the stop() method from Vehicle.

Key Points to Remember

  1. Abstract methods must be implemented by any subclass that extends the abstract class, unless the subclass is also abstract.
  2. Abstract classes provide a mix of concrete methods and abstract methods, allowing you to define common behavior while enforcing a contract for subclasses.
  3. Polymorphism is facilitated by abstract methods, enabling different implementations of the same method in different subclasses.
  4. You cannot instantiate an abstract class directly, but you can create objects of its subclasses.
  5. Abstract methods must be public or protected because they are meant to be overridden by subclasses.

Conclusion

Abstraction through abstract methods in Java allows for the separation of method definitions from their implementations. This promotes flexibility and reusability by defining a common interface for subclasses to implement while allowing each subclass to provide its specific implementation.