![](https://csdnimg.cn/release/download_crawler_static/10874918/bgb.jpg)
DZONE’S GUIDE TO MODERN JAVA DZONE’S GUIDE TO MODERN JAVA
DZONE.COM/GUIDES DZONE’S 2016 GUIDE TO MODERN JAVA
11
DO NOT USE ARRAYS TO PASS VALUES TO AND
FROM THE API
A signicant API mistake was made when the Enum concept
was introduced in Java 5. We all know that an Enum class
has a method called values() that returns an array of
all the Enum’s distinct values. Now, because the Java
framework must ensure that the client code cannot change
the Enum’s values (for example, by directly writing to
the array), a copy of the internal array must be produced
for each call to the value() method. This results in poor
performance and also poor client code usability. If the Enum
would have returned an unmodiable Lis t, that List could
be reused for each call and the client code would have had
access to a better and more useful model of the Enum’s
values. In the general case, consider exposing a Stream, if
the API is to return a collection of elements. This clearly
states that the result is read-only (as opposed to a Lis t
which has a set() method). It also allows the client code to
easily collect the elements in another data structure or act
on them on-the-y. Furthermore, the API can lazily produce
the elements as they become available (e.g. are pulled in
from a le, a socket, or from a database). Again, Java 8’s
improved escape analysis will make sure that a minimum
of objects are actually created on the Java heap. Do not
use arrays as input parameters for methods either, since
this—unless a defensive copy of the array is made—makes
it possible for another thread to modify the content of the
array during method execution.
DO THIS:
public Stream<String> comments() {
return Stream.of(comments);
}
DON’T DO THIS:
public String[] comments() {
return comments; // Exposes the backing array!
}
CONSIDER ADDING STATIC INTERFACE METHODS
TO PROVIDE A SINGLE ENTRY POINT FOR OBJECT
CREATION
Avoid allowing the client code to directly select an
implementation class of an interface. Allowing client code to
create implementation classes directly creates a much more
direct coupling of the API and the client code. It also makes
the API commitment much larger, since now we have to
maintain all the implementation classes exactly as they can
be observed from outside instead of just committing to the
interface as such. Consider adding static interface methods,
to allow the client code to create (potentially specialized)
objects that implement the interface. For example, if we
have an interface Point with two methods int x() and
int y(), then we can expose a static method Point.of(int
x, int y) that produces a (hidden) implementation of the
interface. So, if x and y are both zero, we can return a special
implementation class PointOrigoImpl (with no x or y
elds), or else we return another class PointImpl that holds
the given x and y values. Ensure that the implementation
classes are in another package that are clearly not a part
of the API (e.g. put the Point interface in com.company.
product.shape and the implementations in com.company.
product.internal.shape).
DO THIS:
Point point = Point.of(1,2);
DON’T DO THIS:
Point point = new PointImpl(1,2);
FAVOR COMPOSITION WITH FUNCTIONAL
INTERFACES AND LAMBDAS OVER INHERITANCE
For good reasons, there can only be one super class for
any given Java class. Furthermore, exposing abstract
or base classes in your API that are supposed to be
inherited by client code is a very big and problematic
API commitment. Avoid API inheritance altogether, and
instead consider providing static interface methods that
take one or several lambda parameters and apply those
given lambdas to a default internal API implementation
class. This also creates a much clearer separation of
concerns. For example, instead of inheriting from a public
API class AbstractReader and overriding abstract void
handleError(IOException ioe), it is better to expose
a static method or a builder in the Reader interface that
takes a Consumer<IOException> and applies it to an
internal generic Rea derImpl.
DO THIS:
Reader reader = Reader.builder()
.withErrorHandler(IOException::printStackTrace)
.build();
DON’T DO THIS:
Reader reader = new AbstractReader() {
@Override
public void handleError(IOException ioe) {
ioe. printStackTrace();
}
};
ENSURE THAT YOU ADD THE FunctionalInterface
ANNOTATION TO FUNCTIONAL INTERFACES
Tagging an interface with the @FunctionalInterface
annotation signals that API users may use lambdas to
implement the interface, and it also makes sure the
interface remains usable for lambdas over time by
preventing abstract methods from accidently being added
to the API later on.
DO THIS:
@FunctionalInterface
public interface CircleSegmentConstructor {
CircleSegment apply(Point cntr, Point p, double ang);
// abstract methods cannot be added
}
CONTINUED