Default and Private Methods in Interfaces
Java 8 brought default methods to interfaces, and Java 9 promises to bring in private methods. Let's look at both in action and see how to keep your code clean.
Join the DZone community and get the full member experience.
Join For FreeIn this tutorial, we will look at default and private methods within interfaces. Default methods were added in Java 8, allowing methods to be added to an interface that comes with a default implementation that could be used, overridden, or ignored without causing issues to existing classes that have implemented an interface.
Private methods were missing when default methods were added, preventing code from being split out into smaller methods within an interface. This is something that was a bit off-putting to me, as if you had a default method that became a bit long, there was no way to tidy it up. So now that both default and private methods can exist within an interface, we can write methods like we are used to, although if you haven’t used default methods yet, then you will first need to get past the fact that there is now actual code living in an interface.
In terms of adding default and private methods to an interface, it's really simple. To add a default method, just add the keyword default
to the method definition. I’m not even going to tell you how to add a private method — I don’t want to insult you!
Being so simple, let's just jump straight into some code examples.
public interface MyInterface {
default void defaultMethod() {
privateMethod("Hello from the default method!");
}
private void privateMethod(final String string) {
System.out.println(string);
}
void normalMethod();
}
public class MyClassWithDefaultMethod implements MyInterface {
@Override public void normalMethod() {
System.out.println("Hello from the implemented method!");
}
}
As you can see from the basic snippets above, a default method is defined in the interface, which, in turn, calls a private method. As the name suggests, it is a default method and therefore, due to MyClassWithDefaultMethod
not providing an implementation for defaultMethod
, it will carry on and use what is defined in the interface. So if defaultMethod
and normalMethod
were called, they would produce the following output:
Hello from the default method!
Hello from the implemented method!
There is really not much to it. If you wanted to provide your own implementation of the default method within the class, then, as you would with a normal interface method, just add a method with the same name and preferably pop the @Override
annotation on top of it.
public class MyClassOverrideDefaultMethod implements MyInterface {
@Override public void normalMethod() {
System.out.println("Hello from the implemented method!");
}
@Override public void defaultMethod() {
System.out.println("I have overridden the default method!!");
}
}
There's nothing really to say about this code. Now it looks like a normal class that has implemented an interface. When run, it will produce the following output, rather than what was show previously.
I have overridden the default method!!
Hello from the implemented method!
That's the basics of adding default and private methods to interfaces. Below, we will look a bit more in-depth into default methods as well as cover why to use them in the first place.
So a class can have multiple interfaces, and now an interface can define its own default methods. What happens if a class implements multiple interfaces, and each has the same default method defined? Well, not much happens, as it will fail to compile and produce the following error.
java: class MyClassWithTwoInterfaces inherits unrelated defaults for defaultMethod() from types MyInterface and MyOtherInterface
As you can probably figure out, it cannot determine which default method it should actually use, so it blows up. To get around this situation, we need need to provide an implementation that will override the versions that the interfaces are giving the class. That will be used instead, thus removing the ambiguity that resulted in the error.
Moving onto the next point. A class can override a default interface method and call the original method by using super
, keeping it nicely in line with calling a super method from an extended class. But there is one catch: You need to put the name of the interface before calling super
. This is necessary even if only one interface is added. This makes sense, as super
normally refers to the extended class and, as multiple interfaces are allowed, restricting it to always being called like this keeps it consistent and without any ambiguity. Below is a code snippet of what this would look like.
public class MyClassWithTwoInterfaces implements MyInterface, MyOtherInterface {
@Override public void normalMethod() { // some implementation
}
@Override public void defaultMethod() {
MyInterface.super.defaultMethod();
MyOtherInterface.super.defaultMethod();
}
}
Being able to call super
on a default interface method also resolves the error. It's the same default method from multiple interfaces. You will still need to override the method itself, but you don't need to write much more than that. If one of the original versions satisfies your needs, you can call super
on the implementation you desire and be done with it.
As an interface can extend another interface, what happens when both contain a default method of the same name? I’m sure most of you can figure out the answer, but in case you can’t, it simply takes the implementation from the child interface/the interface furthest down the hierarchy tree.
public interface MyInterface {
default void defaultMethod() {
System.out.println("Hello from the parent interface!");
}
}
public interface MyOtherInterface extends MyInterface {
default void defaultMethod() {
System.out.println("Hello from the child interface!");
}
}
When a class implements MyOtherInterface
and calls defaultMethod
, it will print out:
Hello from the child interface!
As you can see, it has called defaultMethod
from within the child interface MyOtherInterface
. This keeps it in line with providing an implementation to a default method from within a class. If it is included in the child/implementation, then it will use that instead of the original defined on the parent interface.
So why use default methods in the first place? After doing some Googling, it seems that they were added to Java 8 as a way of adding methods in preparation for lambda expressions without breaking code that implemented existing interfaces. So if we had a class that used an interface that was changed — for example, code within a third-party library — we wouldn’t need to worry about any new methods being added to the interface, as our existing code will still work and the new method can be ignored until a later date. By using this example, I believe it is more important for API/library developers to consider using default methods than it is for those who are writing code within their own codebase, where they are in control of everything and can change any classes that have been broken by adding a new interface method.
Below are some code snippets to help make the above point clearer (if it wasn’t already):
public class MyClass implements MyOldInterface {
@Override public void doStuff() { // does stuff
}
}
public interface MyOldInterface {
void doStuff();
}
So here we have a class that is sitting around nice and happy. It has implemented the method defined on the interface and requires no other work to be done. And then we come along without a care in the world and add a new method to the interface.
public interface MyNewInterface {
void doStuff();
void doSomeMore();
}
When we try to compile the code, it will fail — we have not provided an implementation for doSomeMore
. Obviously, this makes us cry as we need to write some extra code, even if it is some half -arsed code just so we can get it to work again. Whereas if the code before was written instead as…
public interface MyNewInterface {
void doStuff();
default void doSomeMore() { // do some more stuff
}
}
...we would remain happy, as our existing class requires no extra work and will still compile. When we eventually decide that we need to implement the new method added to the interface, then we can do so as normal. Furthermore, if the doSomeMore
method was long enough, maybe a private method or two could be used to keep the interface nice and tidy, helping you keep everyone who uses your code happy!
I think that pretty much covers it. In conclusion, default methods were added as part of Java 8 with private methods being an addition in Java 9. Default methods allow an interface to define an implementation for a method so that when a class implements the interface, it does not need to provide its own version of the method, helping APIs and libraries move forward without always needing to make breaking changes when interfaces require additional methods.
Hopefully, this post demonstrated their ease of use, although just because they are simple does not mean they should be added all over the place, and developers only concerned about their own codebase might never find a need to use them. I almost forgot to mention private methods here, but quite frankly, that’s because there's not much to say about them, and if you noticed while going through this post there is barely any mention of them. Private methods in interfaces are there to make code look nicer and give the option of some code reuse.
Published at DZone with permission of Dan Newton, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments