KB

Abstract Classes and Interfaces in Java

Abstract Classes and Interfaces

Abstract Classes

  • An abstract class represents a generic concept in a class hierarchy and is usually insufficiently defined to be useful by itself.
  • It may contain a partial description inherited by all its descendants.
  • An abstract class is like any other class, but it may have methods that are not yet defined.
  • Its children, being more specific, will fill in the gaps.
  • Abstract classes cannot be instantiated and usually contain one or more abstract methods, which have no definition (no code body).
  • Abstract classes can contain non-abstract methods (with method definition) and data declarations.
  • A class is declared as abstract by including the “abstract” modifier in the class header:
    java abstract class class-name { // ... }
  • To declare an abstract method:
    java abstract type name(parameter-list);
    No method body is present.
  • There can be no objects of an abstract class (cannot be directly instantiated with the new operator).
  • Abstract constructors or abstract static methods are not allowed.
  • Any subclass of an abstract class must either implement all abstract methods or be declared abstract itself.
  • A class that contains one or more abstract methods must be declared as abstract.
  • However, a class declared as abstract does not need to contain abstract methods.

Abstract Class Demonstration

abstract class A {
  abstract void callme();

  // Concrete methods are still allowed in abstract classes
  void callmetoo() {
    System.out.println("This is a concrete method.");
  }
}

class B extends A {
  void callme() {
    System.out.println("B's implementation of callme.");
  }
}

public class AbstractDemo {
  public static void main(String args[]) {
    B b = new B();
    b.callme();
    b.callmetoo();
  }
}

Output:

B's implementation of callme.
This is a concrete method.
  • Objects of class A are not declared, as it's not possible to instantiate an abstract class.
  • Class A implements a concrete method callmetoo(), which is perfectly acceptable.
  • Abstract classes can include as much implementation as deemed fit.
  • Although abstract classes cannot be used to instantiate objects, they can be used to create object references.
  • Java’s approach to run-time polymorphism is implemented through superclass references.
  • Thus, it must be possible to create a reference to an abstract class so that it can be used to point to a subclass object.

Abstract Class Example using References

abstract class Bank {
  abstract int getRateOfInterest();
}

class BSP extends Bank {
  int getRateOfInterest() {
    return 3;
  }
}

class KinaBank extends Bank {
  int getRateOfInterest() {
    return 8;
  }
}

public class TestBank {
  public static void main(String args[]) {
    BSP bsp = new BSP();
    KinaBank kb = new KinaBank();
    Bank b;

    // Object Referencing
    b = bsp;
    System.out.println("Rate of Interest is: " + b.getRateOfInterest() + " %");

    b = kb;
    System.out.println("Rate of Interest is: " + b.getRateOfInterest() + " %");
  }
}

Final Keyword with Inheritance

The final keyword has two uses related to inheritance:

  1. Preventing Overriding
  2. Preventing Inheritance

Preventing Overriding

  • To disallow a method from being overridden, specify final as a modifier at the start of its declaration.
  • Methods declared as final cannot be overridden.
class A {
  final void meth() {
    System.out.println("This is a final method.");
  }
}

class B extends A {
  void meth() {
    // ERROR! Can't override.
    System.out.println("Illegal!");
  }
}
  • Attempting to override meth() in class B will result in a compile-time error because it is declared as final in class A.
  • Normally, Java resolves calls to methods dynamically at run time (late binding).
  • However, since final methods cannot be overridden, a call to one can be resolved at compile time (early binding).

Preventing Inheritance

  • To prevent a class from being inherited, precede the class declaration with final.
  • Declaring a class as final implicitly declares all of its methods as final too.
  • It is illegal to declare a class as both abstract and final, because an abstract class is incomplete by itself and relies upon its subclasses to provide complete implementations.
final class A {
  // ...
}

// The following class is illegal.
class B extends A {
  // ERROR! Can't subclass A
  // ...
}
  • It is illegal for B to inherit A since A is declared as final.

Interfaces

  • Using the keyword interface, you can fully abstract a class’ interface from its implementation.
  • You can specify what a class must do, but not how it does it.
  • Interfaces are syntactically similar to classes but lack instance variables, and their methods are declared without any body.
  • You can define interfaces that don’t make assumptions about how they are implemented.
  • Once an interface is defined, any number of classes can implement it.
  • One class can implement any number of interfaces.
  • To implement an interface, a class must create the complete set of methods defined by the interface.
  • Each class is free to determine the details of its own implementation.
  • Java allows you to fully utilize the “one interface, multiple methods” aspect of polymorphism.
  • Interfaces are designed to support dynamic method resolution at run time.

Defining an Interface

  • An interface is defined much like a class.
access interface name {
  return-type method-name1(parameter-list);
  return-type method-name2(parameter-list);
  type final-varname1 = value;
  type final-varname2 = value;
  // ...
  return-type method-nameN(parameter-list);
  type final-varnameN = value;
}
  • access is either public or not used (default access).
    • When no access specifier is included, default access results, and the interface is only available to other members of the package in which it is declared.
    • When it is declared as public, the interface can be used by any other code.
  • name is the name of the interface.
  • Methods declared in an interface have no bodies (abstract methods).
  • There can be no default implementation of any method specified within an interface.
  • Each class that includes an interface must implement all of the methods.
  • Variables can be declared inside of interface declarations.
    • They are implicitly final and static, meaning they cannot be changed by the implementing class.
    • They must also be initialized with a constant value.
  • All methods and variables are implicitly public if the interface itself is declared as public.

Interface Example

interface Callback {
  void callback(int param);
}
  • An interface:
    • Declares only method headers and public constants.
    • Cannot be instantiated.
    • Can be implemented by a class.
    • Cannot extend a class.
    • Can extend several other interfaces.

Implementing Interfaces

  • Once an interface has been defined, one or more classes can implement that interface.
  • To implement an interface, include the implements clause in a class definition, and then create the methods defined by the interface.
access class classname [extends superclass] [implements interface [,interface...]] {
  // class-body
}
  • access is either public or not used.
  • If a class implements more than one interface, the interfaces are separated with a comma.
  • If a class implements two interfaces that declare the same method, then the same method will be used by clients of either interface.
  • The methods that implement an interface must be declared public.
  • The type signature of the implementing method must match exactly the type signature specified in the interface definition.

Callback Interface Implementation

class Client implements Callback {
  // Implement Callback's interface
  public void callback(int p) {
    System.out.println("callback called with " + p);
  }
}
  • When you implement an interface method, it must be declared as public.
  • It is permissible and common for classes that implement interfaces to define additional members of their own.
class Client implements Callback {
  // Implement Callback's interface
  public void callback(int p) {
    System.out.println("callback called with " + p);
  }

  void nonIfaceMeth() {
    System.out.println("Classes that implement interfaces " +
                       "may also define other members, too.");
  }
}

Another Interface Example

interface Bank {
  float rateOfInterest();
}

class BSP implements Bank {
  public float rateOfInterest() {
    return 4.0f;
  }
}

class KinaBnk implements Bank {
  public float rateOfInterest() {
    return 5.5f;
  }
}

class TestInterface2 {
  public static void main(String[] args) {
    Bank b = new BSP();
    System.out.println("ROI: " + b.rateOfInterest());
  }
}

Output:

ROI: 4.0

Accessing Implementations through Interface References

  • Variables can be declared as object references that use an interface rather than a class type.
  • Any instance of any class that implements the declared interface can be referred to by such a variable.
  • When you call a method through one of these references, the correct version will be called based on the actual instance of the interface being referred to.
  • The method to be executed is looked up dynamically at run time, allowing classes to be created later than the code which calls methods on them.

Interface Variables

  • Variables in an interface are treated as static and final by default.
interface Math {
  public static final double PI = 3.14;
  public static final double ELUERNUMBER = 2.718;
  public static final double SQRT = 1.41421;
}

/* Test interface variables */
public class Sample implements Math {
  public static void main(String[] args) {
    // Calculate area of circle
    int radious = 2;

    // call interface constant Math.PI
    double area = Math.PI * radious * radious;
    System.out.println("Area of Circle =" + area);
  }
}

Output:

Area of Circle =12.56
interface Moveable {
  int AVERAGESPEED = 40;

  void move();
}

public class Vehicle implements Moveable {
  public void move() {
    System.out.println("Average speed is" + AVERAGESPEED);
  }

  public static void main(String[] arg) {
    Vehicle vc = new Vehicle();
    vc.move();
    System.out.println("Average speed is" + AVERAGESPEED);
  }
}

Output:

Average speed is 40
Average speed is 40
  • AVERAGESPEED interface variable can be accessed directly without specifying the interface name “Moveable” while accessing through the interface method itself.
  • If we wish to access interface variables through other methods, we need to specify the corresponding interface name, followed by “.”, then the interface variable name, in order to access.

What we declare:

interface Moveable
int AVERAGE-SPEED = 40;
void move();

How the compiler interprets:

interface Moveable
Public static final int AVERAGE-SPEED = 40;
Public abstract void move();

Compiler automatically converts methods of Interface as public and abstract, and the data members as public, static and final by default.

Applying Interfaces

  • There are many ways to implement a stack, but the interface to the stack remains the same (e.g., push() and pop() methods).
  • Because the interface to a stack is separate from its implementation, it is easy to define a stack interface, leaving it to each implementation to define the specifics.
// Define an integer stack interface.
interface IntStack {
  void push(int item); // store an item
  int pop();          // retrieve an item
}

// An Interface implementation of IntStack that uses fixed storage.
class FixedStack implements IntStack {
  private int stck[];
  private int tos;

  // allocate and initialize stack
  FixedStack(int size) {
    stck = new int[size];
    tos = -1;
  }

  // Push an item onto the stack
  public void push(int item) {
    if (tos == stck.length - 1) // use length member
      System.out.println("Stack is full.");
    else
      stck[++tos] = item;
  }

  // Pop an item from the stack
  public int pop() {
    if (tos < 0) {
      System.out.println("Stack underflow.");
      return 0;
    } else
      return stck[tos--];
  }
}

public class IntegerStackTest {
  public static void main(String args[]) {
    FixedStack mystack1 = new FixedStack(5);
    FixedStack mystack2 = new FixedStack(8);
    IntStack mystack3 = new FixedStack(7);

    // Using reference
    // push some numbers onto the stack
    for (int i = 0; i < 5; i++)
      mystack1.push(i);
    for (int i = 0; i < 8; i++)
      mystack2.push(i);

    // pop those numbers off the stack
    System.out.println("Stack in mystack1:");
    for (int i = 0; i < 5; i++)
      System.out.println(mystack1.pop());
    System.out.println("Stack in mystack2:");
    for (int i = 0; i < 8; i++)
      System.out.println(mystack2.pop());
  }
}
  • The class uses the FixedStack implementation through an interface reference.
  • This means that calls to push() and pop() methods are resolved at run time rather than at compile time.