Effective Java 的笔记,代码、英语原文为主,批注、翻译为辅。

Item 1: Consider static factory methods instead of constructors

考虑以静态工厂方法代替构造函数

obtain an instance of the class

获得类实例的方法

provide a public constructor

provide a public static factory method

1
2
3
4
//a static method
public static Boolean valueOf(boolean b) {
	return b ? Boolean.TRUE : Boolean.FALSE;
}

Java source code

The EnumSet class has no public constructors, only static factories. In the OpenJDK implementation, they return an instance of one of two subclasses, depending on the size of the underlying enum type: if it has sixty-four or fewer elements, as most enum types do, the static factories return a RegularEnumSet instance, which is backed by a single long; if the enum type has sixty-five or more elements, the factories return a JumboEnumSet instance, backed by a long array.

EnumSet

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package java.util;

/**
    * Creates an empty enum set with the specified element type.
    *
    * @param <E> The class of the elements in the set
    * @param elementType the class object of the element type for this enum
    *     set
    * @return An empty enum set of the specified type.
    * @throws NullPointerException if {@code elementType} is null
    */
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
    Enum<?>[] universe = getUniverse(elementType);
    if (universe == null)
        throw new ClassCastException(elementType + " not an enum");

    if (universe.length <= 64)
        return new RegularEnumSet<>(elementType, universe);
    else
        return new JumboEnumSet<>(elementType, universe);
}

RegularEnumSet

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package java.util;

/**
 * Private implementation class for EnumSet, for "regular sized" enum types
 * (i.e., those with 64 or fewer enum constants).
 *
 * @author Josh Bloch
 * @since 1.5
 * @serial exclude
 */
class RegularEnumSet<E extends Enum<E>> extends EnumSet<E> {
    @java.io.Serial
    private static final long serialVersionUID = 3411599620347842686L;
    /**
     * Bit vector representation of this set.  The 2^k bit indicates the
     * presence of universe[k] in this set.
     */
    private long elements = 0L;
  
}

JumboEnumSet

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
package java.util;

/**
 * Private implementation class for EnumSet, for "jumbo" enum types
 * (i.e., those with more than 64 elements).
 *
 * @author Josh Bloch
 * @since 1.5
 * @serial exclude
 */
class JumboEnumSet<E extends Enum<E>> extends EnumSet<E> {
    @java.io.Serial
    private static final long serialVersionUID = 334349849919042784L;

    /**
     * Bit vector representation of this set.  The ith bit of the jth
     * element of this array represents the  presence of universe[64*j +i]
     * in this set.
     */
    private long elements[];
  
}

Here are some common names for static factory methods. This list is far from exhaustive:

  • from—A type-conversion method that takes a single parameter and returns a corresponding instance of this type, for example:
    1
    
    Date d = Date.from(instant);
    
  • of—An aggregation method that takes multiple parameters and returns an instance of this type that incorporates them, for example:
    1
    
    Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING);
    
  • valueOf—A more verbose alternative to from and of, for example:
    1
    
    BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
    
  • instance or getInstance—Returns an instance that is described by its parameters (if any) but cannot be said to have the same value, for example:
    1
    
    StackWalker luke = StackWalker.getInstance(options);
    
  • create or newInstance—Like instance or getInstance, except that the method guarantees that each call returns a new instance, for example:
    1
    
    Object newArray = Array.newInstance(classObject, arrayLen);
    
  • getType—Like getInstance, but used if the factory method is in a different class. Type is the type of object returned by the factory method, for example:
    1
    
    FileStore fs = Files.getFileStore(path);
    
  • newType—Like newInstance, but used if the factory method is in a different class. Type is the type of object returned by the factory method, for example:
    1
    
    BufferedReader br = Files.newBufferedReader(path);
    
  • type—A concise alternative to getType and newType, for example:
    1
    
    List<Complaint> litany = Collections.list(legacyLitany);
    

summary

Often static factories are preferable, so avoid the reflex to provide public constructors without first considering static factories.