Abstract Class in Java

An abstract class is a class that is declared abstract. It cannot be instantiated, but they can be sub-classed (extend). Abstract class may or may not include abstract methods (methods without body/implementation).

Let's check our sample below:

Person.java
public abstract class Person {
    private final String firstName;
    private final String lastName;
    private final String phoneNumber;
    private final String email;

    public Person(
            String firstName, String lastName, 
            String phoneNumber, String email) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.phoneNumber = phoneNumber;
        this.email = email;
    }    
    
    /**
     * @return the firstName
     */
    public String getFirstName() {
        return firstName;
    }

    /**
     * @return the lastName
     */
    public String getLastName() {
        return lastName;
    }

    /**
     * @return the phoneNumber
     */
    public String getPhoneNumber() {
        return phoneNumber;
    }
    
    /**
     * @return the email
     */
    public String getEmail() {
        return email;
    }
    
    public void print() {
        printName();
        System.out.println("Phone: " + this.phoneNumber);
        System.out.println("Email: " + email);
    }
    
    abstract void printName();
}
                    

Staff.java
public abstract class Staff extends Person {

    private final String staffId;

    /**
     * @return the staffId
     */
    public String getStaffId() {
        return staffId;
    }

    public Staff(
            String staffId, 
            String firstName, String lastName, 
            String phoneNumber, String email) {
        super(firstName, lastName, phoneNumber, email);
        this.staffId = staffId;
    }
    
    @Override
    public void print() {
        System.out.println("staffId: " + staffId);
        super.print();        
    }
}
                    

Customer.java
public class Customer extends Person {
    
    private final String customerId;

    /**
     * @return the customerId
     */
    public String getCustomerId() {
        return customerId;
    }

    public Customer(
            String customerId, 
            String firstName, String lastName, 
            String phoneNumber, String email) {
        super(firstName, lastName, phoneNumber, email);
        this.customerId = customerId;
    }    
    
    @Override
    public void print() {
        System.out.println("customerId: " + customerId);
        super.print();        
    }

    // must override, class not declared as abstract
    @Override
    void printName() {
        System.out.println("name: " + this.getFirstName() + " " + this.getLastName());
    }
}
                    

In the sample code above, we have three classes: Person, Customer, and Staff. Normally we create classes that can be instantiate (using the new). However, there may be situations where a base class should never be instantiate. Let say, there should only be instances of the Customer class and never an instance of the Person class and even Staff class. This mean, Person class is designated as an abstract. An abstract class is the same as a regular (or concrete) class in all respects except for one: An abstract class can never directly be instantiated.

Try to instantiate Person will giver error: Person is abstract, cannot be instantiated

PersonApp.java
public class PersonApp {

    public static void main(String[] args) {
        // Person is abstract, cannot be instantiated
        // Person person = new Person(
        //         "Tony", "Stark", 
        //         "1234567890", "tony@stark.inc");
        
        Customer customer = new Customer(
                "1", "Tony", "Stark", 
                "1234567890", "tony@stark.inc");
        customer.print();
    }
}

/* 
Output:
------    
customerId: 1
name: Tony Stark
Phone: 1234567890
Email: tony@stark.inc
*/
                    

But Customer is concrete class that can be instantiate.

Abstract class in design patterns are used to encapsulate the behaviors that keeps changing. And how to implement this is, is to give some liberty for sub-classes to implements it's abstract method. In our case (although it's not a good example... sorry) is printName() method.

abstract method

Abstract classes are special in that they can also define abstract methods. Abstract methods are overridable methods that are declared with the abstract keyword and provide no implementation. It just merely a prototype for the method with the following attributes:

  1. A return type
  2. A name
  3. List of parameters (optional)
  4. A throws clause (optional)

A class that inherits from a class with abstract methods must provide an implementation for the abstract methods or must be abstract itself. For example, the Person class above define an abstract printName() method that each sub class has to implement.

Again, Customer is a concrete class that can be instantiate, means Customer class implements printName() method. But Staff class doesn't implement this method, so Staff class must be declared as abstract, and cannot be instantiated.

In above example, Person.print() can call the printName() method, even though Person supplies no implementation for the method, because it is guaranteed that any derived class that can be instantiated must provide an implementation.

Let's continue by creating Employee class (extends from Staff) and provide implementation for printName() method:

Employee.java
public final class Employee extends Staff {

    public Employee(
            String staffId, 
            String firstName, String lastName, 
            String phoneNumber, String email) {
        super(staffId, firstName, lastName, phoneNumber, email);
    }

    @Override
    public void printName() {
        System.out.println("Employee: " + this.getLastName() + " " + this.getFirstName());
    }    
}
                    

And change our PersonApp, now becomes:

PersonApp.java
public class PersonApp {

    public static void main(String[] args) {
        // Person is abstract, cannot be instantiated
        // Person person = new Person(
        //         "Tony", "Stark", 
        //         "1234567890", "tony@stark.inc");
        
        Customer customer = new Customer(
                "1", "Tony", "Stark", 
                "1234567890", "tony@stark.inc");
        customer.print();
        
        Employee employee = new Employee(
                "1", "Peter", "Parker", 
                "4567890123", "peter@stark.inc");
        employee.print();
    }
}

/* 
Output:
------    
customerId: 1
name: Tony Stark
Phone: 1234567890
Email: tony@stark.inc
staffId: 1
Employee: Parker Peter
Phone: 4567890123
Email: peter@stark.inc
*/
                    

And, do you notice that we declared Employee class as final? As opposite to abstract class, final class is ... final, can not be extend anymore, cannot have any subclass.


Let's summarize it:

  • An abstract class cannot be instantiated and need to be extended by subclass.
  • If you declare an abstract method in a class then you must declare the class abstract as well. you can’t have abstract method in a concrete class.
  • An abstract class can have non-abstract method (concrete) as well.
  • Abstract method has no body (implementation).

In other word:

  • A non-abstract class (concrete) can only have non-abstract methods.