Tips
"Effective Java, third Edition" an English version has been published, the second edition of this book presumably many people have read, known as one of the four major Java books, but the second edition of 2009 published, to now nearly 8 years, but with Java 6, 7, 8, and even 9 of the release, the Java language has undergone profound changes.
In the first time here translated into Chinese version. For everyone to learn to share.
21. Designing interfaces for future generations
Prior to Java 8, it was not possible to add a method to an interface without destroying the existing implementation. If a new method is added to the interface, the existing implementation is usually missing the method, resulting in a compile-time error. In Java 8, the default method was added to construct [JLS 9.4] to allow methods to be added to an existing interface. But adding new methods to existing interfaces is fraught with risk.
The declaration of the default method contains a default implementation that allows classes that implement the interface to be used directly, without having to implement the default method. Although adding a default method to Java adds a method to an existing interface, there is no guarantee that these methods can be used in all implementations. The default method is "injected (injected)" into an existing implementation without the knowledge or consent of the implementation class. Prior to Java 8, these implementations were written with the default interface, and their interfaces never obtained any new methods.
Many of the new default methods are added to the core collection interface of Java 8, primarily to facilitate the use of lambda Expressions (chapter 6th). The default method for Java class libraries is a high-quality generic implementation, which in most cases works fine. However, writing a default method is not always possible, it retains all the invariants for each possible implementation .
For example, consider adding a method to the collection interface in Java 8 removeIf
. For example, consider adding a method to the collection interface in Java 8 removeIf
. This method removes Predicate
all elements that return true for the given Boolean method (or function-type interface). The default implementation is specified to use an iterator to iterate through the collection, invoke each element's predicate, and use the iterator's method to remove the element that the remove
predicate returns TRUE. Presumably, this statement looks like this: the default implementation is specified to use iterators to iterate through the collection, invoke the functional interface of each element Predicate
, and use the iterator remove
method to remove the element that returns true for the predicate function interface. According to speculation, this statement looks like this:
// Default method added to the Collection interface in Java 8default boolean removeIf(Predicate<? super E> filter) {????Objects.requireNonNull(filter);????boolean result = false;????for (Iterator<E> it = iterator(); it.hasNext(); ) {????????if (filter.test(it.next())) {????????????it.remove();????????????result = true;????????}????}????return result;}
This is the removeIf
best generic implementation possible for the method, but unfortunately it has failed in some practical collection implementations. For example, consider a org.apache.commons.collections4.collection.SynchronizedCollection
method. This class is derived from the Apache Commons Class library and is similar to the class returned by the static factory method in the Java.util package Collections.synchronizedCollection
. The Apache version also provides the ability to use client-provided objects to lock in place of the collection. In other words, it is a wrapper class (entry 18), and all of their methods are synchronized on a locked object before being delegated to the wrapper collection class.
The Apache SynchronizedCollection
classes are still actively maintained, but the methods are not overridden at the time of this writing removeIf
. If this class is used with Java 81, it inherits removeIf
the default implementation, but does not actually maintain the basic commitment of the class: Automatically synchronizes each method call. The default implementation knows nothing about synchronization and cannot access properties that contain locked objects. If the client invokes a method on the instance while another thread is modifying the collection at the same time SynchronizedCollection
removeIf
, it may result in an ConcurrentModificationException
exception or other unspecified behavior.
To prevent this from happening in a similar Java Platform Class Library implementation, such as Collections.synchronizedCollection
returning a package-level private class, the JDK maintainer must override the default removeIf
implementation and other similar methods to perform the necessary synchronizations before invoking the default implementation. The collection implementations that were not part of the Java platform did not have the opportunity to make similar changes to the interface changes, and some did not.
in the case of the default method, an existing implementation class for an interface can be compiled without errors or warnings, but will fail at run time . Although not very common, this problem is not an isolated event. Some of the methods added to the collection interface in Java 8 are known to be vulnerable, and some existing implementations are known to be affected.
You should avoid adding a new method to an existing interface using the default method, unless the need is critical, in which case you should consider carefully to determine whether an existing interface implementation will be destroyed by the default method implementation. However, the default method is useful for providing a standard method implementation when creating an interface to mitigate the task of implementing an interface (item 20).
It is also worth noting that the default method is not designed to support the removal of a method from an interface or to alter the signature of an existing method. These interfaces are unlikely to change without destroying existing clients.
The guidelines are clear. Although the default method is now part of the Java platform, it is still very important to design the interface very carefully . Although the default method can add a method to an existing interface, this is a significant risk. If an interface contains a small flaw, it may always annoy the user.? If an interface is severely flawed, the API that contains it may be broken.
Therefore, it is important to test each new interface before publishing. Multiple programmers should implement each interface in different ways. At the very least, you should prepare three different implementations. It is equally important to write multiple client programs that use an instance of each new interface to perform various tasks. This will greatly ensure that each interface will meet all of its intended uses. These steps will allow you to discover flaws in the interface before publishing, but you can still easily fix them. Although some defects may be fixed after the interface is published, do not expect it too much .
Effective Java Third edition--21. Designing interfaces for future generations