Can the interface be upgraded?
In the book “Ideas for Java Programming, 4th Edition” :
The interface keyword produces a completely abstract class that does not provide any concrete implementation at all. It allows the author to determine the method name, parameter list, and return type, but without any method body. Interfaces provide only the form, not any concrete implementation.
When we started learning Java, we also know that interfaces only provide declarations of methods, and the concrete implementation must be implemented in the corresponding implementation class. A class that implements an interface must provide an implementation for every method defined in the interface, or it will fail compilation.
As previously defined interfaces become widely implemented, if an interface needs to be upgraded and a new method is added to the interface, all implementation classes will have to be notified to implement the new method. Imagine that you, as an interface implementer, are willing to inexplicably implement methods that you don’t use? It just drives people crazy! Is there no solution?
Wait, you don’t have to be mad. JDK1.8 solves these problems and introduces a new mechanism. The interface in JDK1.8 supports providing an implementation alongside the declaration of methods, which can be done in two ways:
- JDK1.8 allows static methods to be declared within an interface.
- JDK1.8 introduces a new feature called default methods, through which you can specify the default implementation of interface methods.
In other words, an interface can provide a concrete implementation of a method. Therefore, classes that implement interfaces that do not display a concrete implementation that provides the method automatically inherit the default implementation.
The presence of default methods makes it easy and smooth to optimize and upgrade interfaces.
In fact, you have used several default methods since you started using JDK1.8, such as the sort method in the List interface and Stream in the Collection interface.
Public interface List<E> extends Collection<E> {...... default void sort(Comparator<? super E> c) { Object[] a = this.toArray(); Arrays.sort(a, (Comparator) c); ListIterator<E> i = this.listIterator(); for (Object e : a) { i.next(); i.set((E) e); }}... }Copy the code
As you can see, the new sort method has a method body, which is modified by the default modifier, which is the default method of the interface.
I am so happy that interfaces can be upgraded so easily after JDK1.8.
How to upgrade the interface?
If there is an interface Resizable in a drawing library, it defines many methods that a simple scalable shape must support, such as: setHeight, setWidth, getHeight, getWidth, etc.
In addition, you provide several additional implementations, such as squares and rectangles. Because your library is so popular and widely circulated, some of your users use the Resizable interface to create their own implementations of interest, such as ellipses.
The interface Resizable is as follows:
public interface Resizable {
int getWidth();
int getHeight();
void setWidth(int width);
void setHeight(int height);
void setAbsoluteSize(int width, int height);
}
Copy the code
One of your most loyal users implemented the Resizable interface and created the Ellipse class for their own needs:
Public class Ellipse implements Resizable {... }Copy the code
He implemented a game that dealt with various Resizable shapes (including Ellipse) :
A few months after the library went live, you received many requests to update your implementation of Resizable to support the setRelativeSize method for Square, Rectangle, and other shapes. To meet these new requirements, you have to upgrade your interface.
If you add the setRelativeSize method directly to the interface Resizable:
public interface Resizable { int getWidth(); int getHeight(); void setWidth(int width); void setHeight(int height); void setAbsoluteSize(int width, int height); Void setRelativeSize(int wFactor, int hFactor); }Copy the code
This would lead to a series of problems that would require the implementer of the interface (the library user) to force an implementation of the setAbsoluteSize method, which is certainly not a realistic upgrade method.
At this point, we came up with the default method to solve the above problem. Not only to meet the needs of new users, but also compatible with the old users.
public interface Resizable { int getWidth(); int getHeight(); void setWidth(int width); void setHeight(int height); void setAbsoluteSize(int width, int height); // Add the setRelativeSize method, i.e. Default void setRelativeSize(int wFactor, int hFactor){setAbsoluteSize(getWidth()/wFactor, getHeight() / hFactor); }}Copy the code
If you have a new idea of what the setRelativeSize method does, you can override it yourself.
Perfect! Upgrade complete!
The default method is useful
Default methods were introduced to address the evolution of libraries such as Java apis in a compatible manner, as shown in the following figure:
In short, methods in upgrading interfaces are the bane of many problems; When interfaces change, the classes that implement those interfaces often need to be updated, and implementations that provide new methods can adapt to the interface changes.
Now you know how the default method is to update the interface in a compatible way. Besides the usage scenarios above, are there any other scenarios that can take advantage of this new feature? Sure, you can create your own interface and provide default methods for it. Next, there are two scenarios where the default method is used:
-
Optional methods
-
Multiple inheritance
1. Optional method
You’ve probably encountered situations where classes implement interfaces but deliberately leave the implementation of some methods blank. Let’s take the Iterator interface as an example. The Iterator interface defines hasNext, next, and the remove method.
Prior to Java 8, the remove method was often ignored because it was not typically used by users. As a result, classes that implement the Interator interface often put an empty implementation for the remove method, which is useless template code.
By adopting the default method, you can provide a default implementation for this type of method, so that the entity class does not need to explicitly provide an empty method in its own implementation. For example, in Java 8, the Iterator interface provides a default implementation of the remove method, as follows:
interface Iterator<T> { boolean hasNext(); T next(); default void remove() { throw new UnsupportedOperationException(); }}Copy the code
In this way, you can reduce invalid template code. Every class that implements the Iterator interface does not need to declare an empty remove method because it now has a default implementation.
2. Multiple inheritance
The default method allows what was previously unthinkable to happen in an elegant way: multiple inheritance. This is the ability for a class to reuse code from multiple sources, as shown below:
Don’t be fooled into thinking that Java supports multiple inheritance. Remember: “Java classes can only inherit from a single class, but a class can implement multiple interfaces.”
For example, if we’re designing a game, we need to define multiple properties that have different qualities. Some shapes need to be resized, but do not need to be rotated; Some need to be able to rotate and move, but do not need to be resized. How can we design it? So you can use the default method here.
Define a rotatable interface and provide a default method for rotation:
public interface Rotatable { void setRotationAngle(int angleInDegrees); int getRotationAngle(); Default void rotateBy(int angleInDegrees){setRotationAngle((getRotationAngle () + Angle) % 360); }}Copy the code
Define a removable interface and provide a default method for moving:
public interface Moveable { int getX(); int getY(); void setX(int x); void setY(int y); This example demonstrates (int distance){setX(getX() + distance); } default void moveVertically(int distance){ setY(getY() + distance); }}Copy the code
Define a resizable interface and provide a default method for resizing:
public interface Resizable { int getWidth(); int getHeight(); void setWidth(int width); void setHeight(int height); void setAbsoluteSize(int width, int height); default void setRelativeSize(int wFactor, int hFactor){ setAbsoluteSize(getWidth() / wFactor, getHeight() / hFactor); }}Copy the code
You can now create different entity classes for your game, such as Monster that can be moved, rotated, and scaled.
public class Monster implements Rotatable, Moveable, Resizable { //... You only need to give implementations of all the abstract methods. You don't need to implement the default methods repeatedly.Copy the code
Monster inherited the rotateBy, moveHorizontally, moveHorizontally, and setRelativeSize implementations. We can also call different methods directly:
Monster m = new Monster(); // Call rotateBy m.rotateby (180) inherited from Rotatable; | | | | | | | | | | | | | | | | |Copy the code
Suppose you now need to declare another class that can move and rotate, but not scale, such as Sun. Instead of copying and pasting code, you can reuse the default implementation of the Moveable and Rotatable interfaces as follows.
Public class Sun implements Moveable, Rotatable {... }Copy the code
Four,
Through this article, you’ve seen the new Java8 default method introduced, and the flexibility it provides with examples that make it easier to upgrade the methods in the interface, so if you need to make interface changes later in your project, you can also consider using the default method.