Packages and Interfaces  «Prev  Next»

Lesson 7Abstract classes
ObjectiveLearn how abstract classes are close relatives of interfaces.

Abstract Classes and Interfaces in Java 17: A Comparative Analysis

Abstract classes and interfaces are two mechanisms in Java that support abstraction, ensuring that certain structure and behavior contracts are followed by implementing classes. While they have distinct roles and use cases in object-oriented design, there are several similarities between them, especially in the context of Java 17.
  1. Abstraction: Both abstract classes and interfaces are abstract types. This means neither can be directly instantiated to create an object. Their primary purpose is to be extended or implemented by other concrete classes.
    abstract class AbstractClass {}
    interface InterfaceName {}
    
  2. Method Declaration: Both abstract classes and interfaces can declare methods without providing an actual implementation. In abstract classes, these are termed "abstract methods", while in interfaces, all methods were implicitly abstract until Java 8.
    abstract class AbstractClass {
       abstract void abstractMethod();
    }
    
    interface InterfaceName {
       void someMethod(); // implicitly abstract in interface
    }
    
  3. Default Methods: From Java 8 onwards, both abstract classes and interfaces can provide default method implementations. This feature was introduced in interfaces to ensure backward compatibility while extending interface functionalities.
    abstract class AbstractClass {
       void defaultMethod() {
    	   System.out.println("Default in AbstractClass");
       }
    }
    
    interface InterfaceName {
       default void defaultMethod() {
    	   System.out.println("Default in InterfaceName");
       }
    }
    


  4. Static Methods: Java 8 introduced the capability for interfaces to have static methods with implementations. Similarly, abstract classes can also have static methods.
    abstract class AbstractClass {
       static void staticMethod() {
    	   System.out.println("Static in AbstractClass");
       }
    }
    
    interface InterfaceName {
       static void staticMethod() {
    	   System.out.println("Static in InterfaceName");
       }
    }
    
  5. Access Modifiers: Methods in both abstract classes and interfaces can use access modifiers. However, it's important to note that in interfaces, methods are `public` by default and cannot be `private` unless they are static or default methods.
  6. Nested Types: Both abstract classes and interfaces can contain nested types, including inner classes, static nested classes, enums, and interfaces.
  7. Annotations and Modifiers: Both abstract classes and interfaces can be annotated, and both can use a limited set of modifiers, like `public` or `protected` for abstract classes and `public` or `private` for nested members of interfaces.

While abstract classes and interfaces in Java 17 exhibit distinct characteristics and address different design concerns, they share numerous similarities, especially in their ability to define contracts and structures for other classes to follow. Understanding these similarities aids in making informed decisions about when to employ each construct in software design.


Java interfaces are very similar to abstract classes. An abstract class is simply a class that contains unimplemented methods. When a class is derived from an abstract class it must implement all of the unimplemented methods declared in the abstract class.
Unlike interfaces, abstract classes can contain member variables and implemented methods in addition to unimplemented methods. This makes abstract classes more powerful than interfaces in some respects. Java does not support multiple inheritance, which means that you can not derive a class from more than one parent class, including abstract classes. On the other hand, classes can implement as many interfaces as they want. This is a critical difference between interfaces and abstract classes, and ultimately makes interfaces more useful than abstract classes in most situations. For this reason, the Java API relies heavily on interfaces but uses abstract classes sparingly.


Class versus Interface

I use interfaces when I see that something in my design will change frequently.
For example, the Strategy pattern lets you swap new algorithms and processes into your program without altering the objects that use them. A media player might know how to play CDs, MP3s, and wav files. You do not want to hardcode those playback algorithms into the player because that will make it difficult to add a new format such as AVI. Furthermore, your code will be littered with useless case statements. In addition, you will need to update those case statements each time you add a new algorithm. With the Strategy pattern, you can simply encapsulate the algorithm behind an object. If you do that, you can provide new media plug-ins at any time.
Let us call the plug-in class MediaStrategy. That object would have one method: playStream(Stream s). So to add a new algorithm, we simply extend our algorithm class. When the program encounters the new media type, it simply delegates the playing of the stream to our media strategy. Of course, you will need to write code to properly instantiate the algorithm strategies you will need.
This is an excellent place to use an interface. We have used the Strategy pattern, which clearly indicates a place in the design that will change. Thus, you should define the strategy as an interface. You should generally favor interfaces over inheritance when you want an object to have a certain type; in this case, MediaStrategy. Relying on inheritance for type identity is dangerous; it locks you into a particular inheritance hierarchy.