Java 8 allows for default implementation of methods in interfaces called Default Methods.
I am confused between when would I use that sort of interface default method
, instead of an abstract class
(with abstract method(s)
).
So when should interface with default methods be used and when should an abstract class (with abstract method(s)) be used? Are the abstract classes still useful in that scenario?
There's a lot more to abstract classes than default method implementations (such as private state), but as of Java 8, whenever you have the choice of either, you should go with the defender (aka. default
) method in the interface.
The constraint on the default method is that it can be implemented only in the terms of calls to other interface methods, with no reference to a particular implementation's state. So the main use case is higher-level and convenience methods.
The good thing about this new feature is that, where before you were forced to use an abstract class for the convenience methods, thus constraining the implementor to single inheritance, now you can have a really clean design with just the interface and a minimum of implementation effort forced on the programmer.
The original motivation to introduce default
methods to Java 8 was the desire to extend the Collections Framework interfaces with lambda-oriented methods without breaking any existing implementations. Although this is more relevant to the authors of public libraries, you may find the same feature useful in your project as well. You've got one centralized place where to add new convenience and you don't have to rely on how the rest of the type hierarchy looks.
There are a few technical differences. Abstract classes can still do more in comparison to Java 8 interfaces:
Abstract class can have a constructor. Abstract classes are more structured and can hold a state.
Conceptually, main purpose of defender methods is a backward compatibility after introduction of new features (as lambda-functions) in Java 8.
public static final
fields of an interface as "state". The static
part means that the're not related to a particular instance at all. They are assigned on class instantiation, which is not the same as after instance creation.
This is being described in this article. Think about forEach
of Collections.
List<?> list = …
list.forEach(…);
The forEach isn’t declared by java.util.List nor the java.util.Collection interface yet. One obvious solution would be to just add the new method to the existing interface and provide the implementation where required in the JDK. However, once published, it is impossible to add methods to an interface without breaking the existing implementation. The benefit that default methods bring is that now it’s possible to add a new default method to the interface and it doesn’t break the implementations.
AbstractList::forEach
throwing an UnsupportedOperationException
.
As described in this article,
Abstract classes versus interfaces in Java 8
After introducing Default Method, it seems that interfaces and abstract classes are same. However, they are still different concept in Java 8. Abstract class can define constructor. They are more structured and can have a state associated with them. While in contrast, default method can be implemented only in the terms of invoking other interface methods, with no reference to a particular implementation's state. Hence, both use for different purposes and choosing between two really depends on the scenario context.
Whenever we have a choice between abstract class and interface we should always (almost) prefer default (also known as defender or virtual extensions) methods.
Default methods have put an end to classic pattern of interface and a companion class that implements most or all of the methods in that interface. An example is Collection and AbstractCollection. Now we should implement the methods in the interface itself to provide default functionality. The classes which implement the interface has choice to override the methods or inherit the default implementation. Another important use of default methods is interface evolution. Suppose I had a class Ball as: public class Ball implements Collection { ... }
Now in Java 8 a new feature streams in introduced. We can get a stream by using stream
method added to the interface. If stream
were not a default method all the implementations for Collection
interface would have broken as they would not be implementing this new method. Adding a non-default method to an interface is not source-compatible
.
But suppose we do not recompile the class and use an old jar file which contains this class Ball
. The class will load fine without this missing method, instances can be created and it seems everything is working fine. BUT if program invokes stream
method on instance of Ball
we will get AbstractMethodError
. So making method default solved both the problems.
Java 9 has got even private methods in interface which can be used to encapsulate the common code logic that was used in the interface methods that provided a default implementation.
These two are quite different:
Default methods are to add external functionality to existing classes without changing their state.
And abstract classes are a normal type of inheritance, they are normal classes which are intended to be extended.
Although its an old question let me give my input on it as well.
abstract class: Inside abstract class we can declare instance variables, which are required to the child class Interface: Inside interface every variables is always public static and final we cannot declare instance variables abstract class: Abstract class can talk about state of object Interface: Interface can never talk about state of object abstract class: Inside Abstract class we can declare constructors Interface: Inside interface we cannot declare constructors as purpose of constructors is to initialize instance variables. So what is the need of constructor there if we cannot have instance variables in interfaces. abstract class: Inside abstract class we can declare instance and static blocks Interface: Interfaces cannot have instance and static blocks. abstract class: Abstract class cannot refer lambda expression Interfaces: Interfaces with single abstract method can refer lambda expression abstract class: Inside abstract class we can override OBJECT CLASS methods Interfaces: We cannot override OBJECT CLASS methods inside interfaces.
I will end on the note that:
Default method concepts/static method concepts in interface came just to save implementation classes but not to provide meaningful useful implementation. Default methods/static methods are kind of dummy implementation, "if you want you can use them or you can override them (in case of default methods) in implementation class" Thus saving us from implementing new methods in implementation classes whenever new methods in interfaces are added. Therefore interfaces can never be equal to abstract classes.
Regarding your query of
So when should interface with default methods be used and when should an abstract class be used? Are the abstract classes still useful in that scenario?
java documentation provides perfect answer.
Abstract Classes Compared to Interfaces:
Abstract classes are similar to interfaces. You cannot instantiate them, and they may contain a mix of methods declared with or without an implementation. However, with abstract classes, you can declare fields that are not static and final, and define public, protected, and private concrete methods. With interfaces, all fields are automatically public, static, and final, and all methods that you declare or define (as default methods) are public. In addition, you can extend only one class, whether or not it is abstract, whereas you can implement any number of interfaces.
Use cases for each of them have been explained in below SE post:
What is the difference between an interface and abstract class?
Are the abstract classes still useful in that scenario?
Yes. They are still useful. They can contain non-static, non-final methods and attributes (protected, private in addition to public), which is not possible even with Java-8 interfaces.
Default methods in Java interface enables interface evolution.
Given an existing interface, if you wish to add a method to it without breaking the binary compatibility with older versions of the interface, you have two options at hands: add a default or a static method. Indeed, any abstract method added to the interface would have to be impleted by the classes or interfaces implementing this interface.
A static method is unique to a class. A default method is unique to an instance of the class.
If you add a default method to an existing interface, classes and interfaces which implement this interface do not need to implement it. They can
implement the default method, and it overrides the implementation in implemented interface.
re-declare the method (without implementation) which makes it abstract.
do nothing (then the default method from implemented interface is simply inherited).
More on the topic here.
Remi Forax rule is You don't design with Abstract classes. You design your app with interfaces. Watever is the version of Java, whatever is the language. It is backed by the Interface segregation principle in SOLID principles.
You can later use Abstract classes to factorize code. Now with Java 8 you can do it directly in the interface. This is a facility, not more.
when should interface with default methods be used and when should an abstract class be used?
Backward compatibility: Imagine that your interface is implemented by hundreds of classes, modifying that interface will force all the users to implement the newly added method, even though it could be not essential for many other classes that implements your interface, Plus it allows your interface to be a functional interface
Facts & Restrictions:
1-May only be declared within an interface and not within a class or abstract class.
2-Must provide a body
3-It is not assumed to be abstract as other normal methods used in an interface.
In Java 8, an interface looks like an abstract class although their might be some differences such as :
1) Abstract classes are classes, so they are not restricted to other restrictions of the interface in Java e.g. abstract class can have the state, but you cannot have the state on the interface in Java.
2) Another semantic difference between interface with default methods and abstract class is that you can define constructors inside an abstract class, but you cannot define constructor inside interface in Java
Default methods in Java Interface are to be used more for providing dummy implementation of a function thus saving any implementing class of that interface from the pain of declaring all the abstract methods even if they want to deal with only one. Default methods in interface are thus in a way more a replacement for the concept of adapter classes.
The methods in abstract class are however supposed to give a meaningful implementation which any child class should override only if needed to override a common functionality.
As mentioned in other answers, the ability to add implementation to an interface was added in order to provide backward compatibility in the Collections framework. I would argue that providing backward compatibility is potentially the only good reason for adding implementation to an interface.
Otherwise, if you add implementation to an interface, you are breaking the fundamental law for why interfaces were added in the first place. Java is a single inheritance language, unlike C++ which allows for multiple inheritance. Interfaces provide the typing benefits that come with a language that supports multiple inheritance without introducing the problems that come with multiple inheritance.
More specifically, Java only allows single inheritance of an implementation, but it does allow multiple inheritance of interfaces. For example, the following is valid Java code:
class MyObject extends String implements Runnable, Comparable { ... }
MyObject
inherits only one implementation, but it inherits three contracts.
Java passed on multiple inheritance of implementation because multiple inheritance of implementation comes with a host of thorny problems, which are outside the scope of this answer. Interfaces were added to allow multiple inheritance of contracts (aka interfaces) without the problems of multiple inheritance of implementation.
To support my point, here is a quote from Ken Arnold and James Gosling from the book The Java Programming Language, 4th edition:
Single inheritance precludes some useful and correct designs. The problems of multiple inheritance arise from multiple inheritance of implementation, but in many cases multiple inheritance is used to inherit a number of abstract contracts and perhaps one concrete implementation. Providing a means to inherit an abstract contract without inheriting an implementation allows the typing benefits of multiple inheritance without the problems of multiple implementation inheritance. The inheritance of an abstract contract is termed interface inheritance. The Java programming language supports interface inheritance by allowing you to declare an interface type
From a business use case context , Interfaces can be used to define specific business rules , where as abstract class would define the common structure to kickoff the business.
Say some business owner wants to partner with Amazon and Walmart , then the interfaces defined here would be WalmartPartner
and AmazonPartner
would define the specific business rules and the abstract class BusinessSetup
will get the business setup in a specific region.
// Interfaces
public interface WalmartPartner {
public static boolean signUpForWalmartBusinessAccount(String BusinessId){
System.out.println("Setting up Walmart Business Partner");
return true;
}
public default void getWalmartDeals(){
System.out.println("Default walmart deal executed !");
}
public abstract void setupShopifyForWalmart();
public abstract void setupWalmartProducts();
public interface AmazonPartner {
public static boolean signUpAsAmazonServicePartner(String BusinessId){
System.out.println("Setting up Amazon Business Partner");
return true;
}
public default void paymentPlatformSetup(){
System.out.println(" Amazon default payment platform is setup");
}
public abstract void setupPrimeMemberDealsByRegion();
public abstract void setupPrimeDeals();
}
// Abstract class
public abstract class BusinessSetup {
String businessId ;
public BusinessSetup(String businessId){
this.businessId = businessId;
System.out.println("1. Initial Business setup for BusienssID: "+this.businessId+" is Complete");
}
public final boolean getBusinessRegisteredInRegion(String region){
System.out.println("2. Business got registered in "+region+ "!");
return true;
}
public abstract void setupCustomerPlatform(String customerId);
public abstract void setupVendorPlatform(String vendorId);
}
// Concrete Class
public class WalMartPartnerImpl extends BusinessSetup implements WalmartPartner {
public WalMartPartnerImpl(String businessId) {
super(businessId);
}
@Override
public void setupCustomerPlatform(String customerId) {
}
@Override
public void setupVendorPlatform(String vendorId) {
}
@Override
public void setupShopifyForWalmart() {
}
@Override
public void setupWalmartProducts() {
}
public static void main(String args[]){
WalMartPartnerImpl walMartPartner = new WalMartPartnerImpl("wal8989");
walMartPartner.getBusinessRegisteredInRegion("california");
walMartPartner.getWalmartDeals();
walMartPartner.setupCustomerPlatform("wal8989");
}
}
Please think first of open/closed principle. The default methods in interfaces DO VIOLATE it. This is a bad feature in Java. It encourages bad design, bad architecture, low software quality. I would suggest to avoid using default methods completely.
Ask yourself a few questions: Why can't you put your methods to the abstract class? Would you need then more than one abstract class? Then think about what is your class responsible for. Are you sure all methods you are going to put to the single class really fulfill the same purpose? May be you will distinguish several purposes and will then split your class into several classes, for each purpose its own class.
Success story sharing