换句话说,如果一个参数化类型代表一个T生产者,使用<? extends T>;如果它代表T消费者,则使用<? super T>。 在我们的Stack示例中,pushAll方法的src参数生成栈使用的E实例,因此src的合适类型为Iterable<? extends E>;popAll方法的dst参数消费Stack中的E实例,因此dst的合适类型是Collection <? super E>。 PECS助记符抓住了使用通配符类型的基本原则。 Naftalin和Wadler称之为获取和放置原则( Get and Put Principle )[Naftalin07,2.4]。
static <T> T[] pickTwo(T a, T b, T c) { switch(ThreadLocalRandom.current().nextInt(3)) { case 0: return toArray(a, b); case 1: return toArray(a, c); case 2: return toArray(b, c); } throw new AssertionError(); // Can't get here }
// List as a typesafe alternative to a generic varargs parameter static <T> List<T> flatten(List<List<? extends T>> lists) { List<T> result = new ArrayList<>(); for (List<? extends T> list : lists) result.addAll(list); return result; }
static <T> List<T> pickTwo(T a, T b, T c) { switch(rnd.nextInt(3)) { case 0: return List.of(a, b); case 1: return List.of(a, c); case 2: return List.of(b, c); } throw new AssertionError(); }
// Typesafe heterogeneous container pattern - API public class Favorites { public <T> void putFavorite(Class<T> type, T instance); public <T> T getFavorite(Class<T> type); }
// Achieving runtime type safety with a dynamic cast public <T> void putFavorite(Class<T> type, T instance) { favorites.put(type, type.cast(instance)); }
// The int enum pattern - severely deficient! public static final int APPLE_FUJI = 0; public static final int APPLE_PIPPIN = 1; public static final int APPLE_GRANNY_SMITH = 2; public static final int ORANGE_NAVEL = 0; public static final int ORANGE_TEMPLE = 1; public static final int ORANGE_BLOOD = 2;
// Enum type with data and behavior public enum Planet { MERCURY(3.302e+23, 2.439e6), VENUS (4.869e+24, 6.052e6), EARTH (5.975e+24, 6.378e6), MARS (6.419e+23, 3.393e6), JUPITER(1.899e+27, 7.149e7), SATURN (5.685e+26, 6.027e7), URANUS (8.683e+25, 2.556e7), NEPTUNE(1.024e+26, 2.477e7);
private final double mass; // In kilograms private final double radius; // In meters private final double surfaceGravity; // In m / s^2 // Universal gravitational constant in m^3 / kg s^2 private static final double G = 6.67300E-11;
public class WeightTable { public static void main(String[] args) { double earthWeight = Double.parseDouble(args[0]); double mass = earthWeight / Planet.EARTH.surfaceGravity(); for (Planet p : Planet.values()) System.out.printf("Weight on %s is %f%n", p, p.surfaceWeight(mass)); } }
Weight on MERCURY is 69.912739 Weight on VENUS is 167.434436 Weight on EARTH is 185.000000 Weight on MARS is 70.226739 Weight on JUPITER is 467.990696 Weight on SATURN is 197.120111 Weight on URANUS is 167.398264 Weight on NEPTUNE is 210.208751
// Enum type that switches on its own value - questionable public enum Operation { PLUS, MINUS, TIMES, DIVIDE;
// Do the arithmetic operation represented by this constant public double apply(double x, double y) { switch(this) { case PLUS: return x + y; case MINUS: return x - y; case TIMES: return x * y; case DIVIDE: return x / y; } throw new AssertionError("Unknown op: " + this); } }
// Enum type with constant-specific method implementations public enum Operation { PLUS {public double apply(double x, double y){return x + y;}}, MINUS {public double apply(double x, double y){return x - y;}}, TIMES {public double apply(double x, double y){return x * y;}}, DIVIDE{public double apply(double x, double y){return x / y;}};
public abstract double apply(double x, double y); }
@Override public String toString() { return symbol; }
public abstract double apply(double x, double y); }
显示的toString实现可以很容易地打印算术表达式,正如这个小程序所展示的那样:
1 2 3 4 5 6 7
public static void main(String[] args) { double x = Double.parseDouble(args[0]); double y = Double.parseDouble(args[1]); for (Operation op : Operation.values()) System.out.printf("%f %s %f = %f%n", x, op, y, op.apply(x, y)); }
// Implementing a fromString method on an enum type private static final Map<String, Operation> stringToEnum = Stream.of(values()).collect( toMap(Object::toString, e -> e));
// Returns Operation for string, if any public static Optional<Operation> fromString(String symbol) { return Optional.ofNullable(stringToEnum.get(symbol)); }
// Switch on an enum to simulate a missing method public static Operation inverse(Operation op) { switch(op) { case PLUS: return Operation.MINUS; case MINUS: return Operation.PLUS; case TIMES: return Operation.DIVIDE; case DIVIDE: return Operation.TIMES;
default: throw new AssertionError("Unknown op: " + op); } }
// Bit field enumeration constants - OBSOLETE! public class Text { public static final int STYLE_BOLD = 1 << 0; // 1 public static final int STYLE_ITALIC = 1 << 1; // 2 public static final int STYLE_UNDERLINE = 1 << 2; // 4 public static final int STYLE_STRIKETHROUGH = 1 << 3; // 8
// Parameter is bitwise OR of zero or more STYLE_ constants public void applyStyles(int styles) { ... } }
// Erroneous insertion of coin into stamp collection stamps.add(new Coin( ... )); // Emits "unchecked call" warning
直到您尝试从stamp集合中检索coin实例时才会发生错误:
1 2 3 4
// Raw iterator type - don't do this! for (Iterator i = stamps.iterator(); i.hasNext(); ) Stamp stamp = (Stamp) i.next(); // Throws ClassCastException stamp.cancel();
// Fails at runtime - unsafeAdd method uses a raw type (List)! public static void main(String[] args) { List<String> strings = new ArrayList<>(); unsafeAdd(strings, Integer.valueOf(42)); String s = strings.get(0); // Has compiler-generated cast }
// Use of raw type for unknown element type - don't do this! static int numElementsInCommon(Set s1, Set s2) { int result = 0; for (Object o1 : s1) if (s2.contains(o1)) result++; return result; }
WildCard.java:13: error: incompatible types: String cannot be converted to CAP#1 c.add("verboten"); ^ where CAP#1 is a fresh type-variable: CAP#1 extends Object from capture of ?
// Adding local variable to reduce scope of @SuppressWarnings public <T> T[] toArray(T[] a) { if (a.length < size) { // This cast is correct because the array we're creating // is of the same type as the one passed in, which is T[]. @SuppressWarnings("unchecked") T[] result = (T[]) Arrays.copyOf(elements, size, a.getClass()); return result; } System.arraycopy(elements, 0, a, 0, size); if (a.length > size) a[size] = null; return a; }
// A first cut at making Chooser generic - won't compile public class Chooser<T> { private final T[] choiceArray;
public Chooser(Collection<T> choices) { choiceArray = choices.toArray(); }
// choose method unchanged }
如果你尝试编译这个类,会得到这个错误信息:
1 2 3 4 5 6
Chooser.java:9: error: incompatible types: Object[] cannot be converted to T[] choiceArray = choices.toArray(); ^ where T is a type-variable: T extends Object declared in class Chooser
没什么大不了的,将Object数组转换为T数组:
1
choiceArray = (T[]) choices.toArray();
这没有了错误,而是得到一个警告:
1 2 3 4 5 6
Chooser.java:9: warning: [unchecked] unchecked cast choiceArray = (T[]) choices.toArray(); ^ required: T[], found: Object[] where T is a type-variable: T extends Object declared in class Chooser
// Object-based collection - a prime candidate for generics public class Stack { private Object[] elements; private int size = 0; private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Stack() { elements = new Object[DEFAULT_INITIAL_CAPACITY]; }
public void push(Object e) { ensureCapacity(); elements[size++] = e; }
public Object pop() { if (size == 0) throw new EmptyStackException(); Object result = elements[--size]; elements[size] = null; // Eliminate obsolete reference return result; }
public boolean isEmpty() { return size == 0; }
private void ensureCapacity() { if (elements.length == size) elements = Arrays.copyOf(elements, 2 * size + 1); } }
// Initial attempt to generify Stack - won't compile! public class Stack<E> { private E[] elements; private int size = 0; private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Stack() { elements = new E[DEFAULT_INITIAL_CAPACITY]; }
public void push(E e) { ensureCapacity(); elements[size++] = e; }
public E pop() { if (size == 0) throw new EmptyStackException(); E result = elements[--size]; elements[size] = null; // Eliminate obsolete reference return result; } ... // no changes in isEmpty or ensureCapacity }
你通常会得到至少一个错误或警告,这个类也不例外。 幸运的是,这个类只产生一个错误:
1 2 3
Stack.java:8: generic array creation elements = new E[DEFAULT_INITIAL_CAPACITY]; ^
// The elements array will contain only E instances from push(E). // This is sufficient to ensure type safety, but the runtime // type of the array won't be E[]; it will always be Object[]! @SuppressWarnings("unchecked") public Stack() { elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY]; }
// Little program to exercise our generic Stack public static void main(String[] args) { Stack<String> stack = new Stack<>(); for (String arg : args) stack.push(arg); while (!stack.isEmpty()) System.out.println(stack.pop().toUpperCase()); }
// Constant interface antipattern - do not use! public interface PhysicalConstants { // Avogadro's number (1/mol) static final double AVOGADROS_NUMBER = 6.022_140_857e23;
// Boltzmann constant (J/K) static final double BOLTZMANN_CONSTANT = 1.380_648_52e-23;
// Mass of the electron (kg) static final double ELECTRON_MASS = 9.109_383_56e-31; }
// Constant utility class package com.effectivejava.science;
public class PhysicalConstants { private PhysicalConstants() { } // Prevents instantiation
public static final double AVOGADROS_NUMBER = 6.022_140_857e23; public static final double BOLTZMANN_CONST = 1.380_648_52e-23; public static final double ELECTRON_MASS = 9.109_383_56e-31; }
// Public class with exposed immutable fields - questionable
public final class Time { private static final int HOURS_PER_DAY = 24; private static final int MINUTES_PER_HOUR = 60; public final int hour; public final int minute;
public Time(int hour, int minute) { if (hour < 0 || hour >= HOURS_PER_DAY) throw new IllegalArgumentException("Hour: " + hour); if (minute < 0 || minute >= MINUTES_PER_HOUR) throw new IllegalArgumentException("Min: " + minute); this.hour = hour; this.minute = minute; }
public static final Complex ZERO = new Complex(0, 0); public static final Complex ONE = new Complex(1, 0); public static final Complex I = new Complex(0, 1);
对于一些类来说,不变性是不切实际的。如果一个类不能设计为不可变类,那么也要尽可能地限制它的可变性。减少对象可以存在的状态数量,可以更容易地分析对象,以及降低出错的可能性。因此,除非有足够的理由把属性设置为非 final 的情况下,否则应该每个属性都设置为 final 的。把本条目的建议与条目15的建议结合起来,你自然的倾向就是:除非有充分的理由不这样做,否则应该把每个属性声明为私有final的。
// Broken - Inappropriate use of inheritance! public class InstrumentedHashSet<E> extends HashSet<E> { // The number of attempted element insertions private int addCount = 0;
public InstrumentedHashSet() { }
public InstrumentedHashSet(int initCap, float loadFactor) { super(initCap, loadFactor); } @Override public boolean add(E e) { addCount++; return super.add(e); } @Override public boolean addAll(Collection<? extends E> c) { addCount += c.size(); return super.addAll(c); } public int getAddCount() { return addCount; } }
// Reusable forwarding class import java.util.Collection; import java.util.Iterator; import java.util.Set;
public class ForwardingSet<E> implements Set<E> {
private final Set<E> s;
public ForwardingSet(Set<E> s) { this.s = s; }
public void clear() { s.clear(); }
public boolean contains(Object o) { return s.contains(o); }
public boolean isEmpty() { return s.isEmpty(); }
public int size() { return s.size(); }
public Iterator<E> iterator() { return s.iterator(); }
public boolean add(E e) { return s.add(e); }
public boolean remove(Object o) { return s.remove(o); }
public boolean containsAll(Collection<?> c) { return s.containsAll(c); }
public boolean addAll(Collection<? extends E> c) { return s.addAll(c); }
public boolean removeAll(Collection<?> c) { return s.removeAll(c); }
public boolean retainAll(Collection<?> c) { return s.retainAll(c); }
public Object[] toArray() { return s.toArray(); }
public <T> T[] toArray(T[] a) { return s.toArray(a); }
@Override public boolean equals(Object o) { return s.equals(o); }
@Override public int hashCode() { return s.hashCode(); }
@Override public String toString() { return s.toString(); } } // Wrapper class - uses composition in place of inheritance import java.util.Collection; import java.util.Set;
public class InstrumentedSet<E> extends ForwardingSet<E> {
private int addCount = 0;
public InstrumentedSet(Set<E> s) { super(s); } @Override public boolean add(E e) { addCount++; return super.add(e); }
@Override public boolean addAll(Collection<? extends E> c) { addCount += c.size(); return super.addAll(c); }
public boolean remove(Object o) Removes a single instance of the specified element from this collection, if it is present (optional operation). More formally, removes an element e such that Objects.equals(o, e), if this collection contains one or more such elements. Returns true if this collection contained the specified element (or equivalently, if this collection changed as a result of the call).
Implementation Requirements: This implementation iterates over the collection looking for the specified element. If it finds the element, it removes the element from the collection using the iterator’s remove method. Note that this implementation throws an UnsupportedOperationException if the iterator returned by this collection’s iterator method does not implement the remove method and this collection contains the specified object.
protected void removeRange(int fromIndex, int toIndex) Removes from this list all of the elements whose index is between fromIndex, inclusive, and toIndex, exclusive. Shifts any succeeding elements to the left (reduces their index). This call shortens the list by (toIndex - fromIndex) elements. (If toIndex == fromIndex, this operation has no effect.) This method is called by the clear operation on this list and its sublists. Overriding this method to take advantage of the internals of the list implementation can substantially improve the performance of the clear operation on this list and its sublists. Implementation Requirements: This implementation gets a list iterator positioned before fromIndex and repeatedly calls ListIterator.nextfollowed by ListIterator.remove, until the entire range has been removed. Note: If ListIterator.remove requires linear time, this implementation requires quadratic time. Parameters: fromIndex index of first element to be removed.
声明一个 int 类型的变量result,并将其初始化为对象中第一个重要属性c的哈希码,如下面步骤2.a中所计算的那样。(回顾条目10,重要的属性是影响比较相等的领域。)
对于对象中剩余的重要属性f,请执行以下操作:
a. 比较属性f与属性c的 int 类型的哈希码: – i. 如果这个属性是基本类型的,使用Type.hashCode(f)方法计算,其中Type类是对应属性 f 基本类型的包装类。 – ii 如果该属性是一个对象引用,并且该类的equals方法通过递归调用equals来比较该属性,并递归地调用hashCode方法。 如果需要更复杂的比较,则计算此字段的“范式(“canonical representation)”,并在范式上调用hashCode。 如果该字段的值为空,则使用0(也可以使用其他常数,但通常来使用0表示)。 – iii 如果属性f是一个数组,把它看作每个重要的元素都是一个独立的属性。 也就是说,通过递归地应用这些规则计算每个重要元素的哈希码,并且将每个步骤2.b的值合并。 如果数组没有重要的元素,则使用一个常量,最好不要为0。如果所有元素都很重要,则使用Arrays.hashCode方法。
b. 将步骤2.a中属性c计算出的哈希码合并为如下结果:result = 31 * result + c;
/** * Returns the string representation of this phone number. * The string consists of twelve characters whose format is * "XXX-YYY-ZZZZ", where XXX is the area code, YYY is the * prefix, and ZZZZ is the line number. Each of the capital * letters represents a single decimal digit. * * If any of the three parts of this phone number is too small * to fill up its field, the field is padded with leading zeros. * For example, if the value of the line number is 123, the last * four characters of the string representation will be "0123". */ @Override public String toString() { return String.format("%03d-%03d-%04d", areaCode, prefix, lineNum); }
如果你决定不指定格式,那么文档注释应该是这样的:
1 2 3 4 5 6 7 8
/** * Returns a brief description of this potion. The exact details * of the representation are unspecified and subject to change, * but the following may be regarded as typical: * * "[Potion #9: type=love, smell=turpentine, look=india ink]" */ @Override public String toString() { ... }
// Clone method for class with no references to mutable state @Override public PhoneNumber clone() { try { return (PhoneNumber) super.clone(); } catch (CloneNotSupportedException e) { throw new AssertionError(); // Can't happen } }
// Ensure space for at least one more element. private void ensureCapacity() { if (elements.length == size) elements = Arrays.copyOf(elements, 2 * size + 1); } }
// Clone method for class with references to mutable state @Override public Stack clone() { try { Stack result = (Stack) super.clone(); result.elements = elements.clone(); return result; } catch (CloneNotSupportedException e) { throw new AssertionError(); } }
// Iteratively copy the linked list headed by this Entry Entry deepCopy() { Entry result = new Entry(key, value, next); for (Entry p = result; p.next != null; p = p.next) p.next = new Entry(p.next.key, p.next.value, p.next.next); return result; }
// clone method for extendable class not supporting Cloneable @Override protected final Object clone() throws CloneNotSupportedException { throw new CloneNotSupportedException(); }
// Single-field Comparable with object reference field public final class CaseInsensitiveString implements Comparable<CaseInsensitiveString> { public int compareTo(CaseInsensitiveString cis) { return String.CASE_INSENSITIVE_[ORDER.compare(s](http://ORDER.compare(s), cis.s); } ... // Remainder omitted }
// Multiple-field Comparable with primitive fields public int compareTo(PhoneNumber pn) { int result = [Short.compare(areaCode](http://Short.compare(areaCode), pn.areaCode); if (result == 0) { result = [Short.compare(prefix](http://Short.compare(prefix), pn.prefix); if (result == 0) result = [Short.compare(lineNum](http://Short.compare(lineNum), pn.lineNum); } return result; }
// Reusing expensive object for improved performance public class RomanNumerals { private static final Pattern ROMAN = Pattern.compile( "^(?=.)M*(C[MD]|D?C{0,3})" + "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");
// Hideously slow! Can you spot the object creation? private static long sum() { Long sum = 0L; for (long i = 0; i <= Integer.MAX_VALUE; i++) sum += i; return sum; }
// Can you spot the "memory leak"? public class Stack { private Object[] elements; private int size = 0; private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Stack() { elements = new Object[DEFAULT_INITIAL_CAPACITY]; }
public void push(Object e) { ensureCapacity(); elements[size++] = e; }
public Object pop() { if (size == 0) throw new EmptyStackException(); return elements[--size]; }
/** * Ensure space for at least one more element, roughly * doubling the capacity each time the array needs to grow. */ private void ensureCapacity() { if (elements.length == size) elements = Arrays.copyOf(elements, 2 * size + 1); } }
这个程序没有什么明显的错误(但是对于泛型版本,请参阅条目 29)。 你可以对它进行详尽的测试,它都会成功地通过每一项测试,但有一个潜在的问题。 笼统地说,程序有一个“内存泄漏”,由于垃圾回收器的活动的增加,或内存占用的增加,静默地表现为性能下降。 在极端的情况下,这样的内存泄漏可能会导致磁盘分页( disk paging),甚至导致内存溢出(OutOfMemoryError)的失败,但是这样的故障相对较少。
finalizer机制有一个严重的安全问题:它们会打开你的类来进行finalizer机制攻击。finalizer机制攻击的想法很简单:如果一个异常是从构造方法或它的序列化中抛出的——readObject和readResolve方法(第12章)——恶意子类的finalizer机制可以运行在本应该“中途夭折(died on the vine)”的部分构造对象上。finalizer机制可以在静态字属性记录对对象的引用,防止其被垃圾收集。一旦记录了有缺陷的对象,就可以简单地调用该对象上的任意方法,而这些方法本来就不应该允许存在。从构造方法中抛出异常应该足以防止对象出现;而在finalizer机制存在下,则不是。这样的攻击会带来可怕的后果。Final类不受finalizer机制攻击的影响,因为没有人可以编写一个final类的恶意子类。为了保护非final类不受finalizer机制攻击,编写一个final的finalize方法,它什么都不做。
// An autocloseable class using a cleaner as a safety net public class Room implements AutoCloseable { private static final Cleaner cleaner = Cleaner.create();
// Resource that requires cleaning. Must not refer to Room! private static class State implements Runnable { int numJunkPiles; // Number of junk piles in this room
// try-finally - No longer the best way to close resources! static String firstLineOfFile(String path) throws IOException { BufferedReader br = new BufferedReader(new FileReader(path)); try { return br.readLine(); } finally { br.close(); } }
这可能看起来并不坏,但是当添加第二个资源时,情况会变得更糟:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// try-finally is ugly when used with more than one resource! static void copy(String src, String dst) throws IOException { InputStream in = new FileInputStream(src); try { OutputStream out = new FileOutputStream(dst); try { byte[] buf = new byte[BUFFER_SIZE]; int n; while ((n = in.read(buf)) >= 0) out.write(buf, 0, n); } finally { out.close(); } } finally { in.close(); } }
// try-with-resources - the the best way to close resources! static String firstLineOfFile(String path) throws IOException { try (BufferedReader br = new BufferedReader( new FileReader(path))) { return br.readLine(); } }
以下是我们的第二个使用try-with-resources的示例:
1 2 3 4 5 6 7 8 9 10
// try-with-resources on multiple resources - short and sweet static void copy(String src, String dst) throws IOException { try (InputStream in = new FileInputStream(src); OutputStream out = new FileOutputStream(dst)) { byte[] buf = new byte[BUFFER_SIZE]; int n; while ((n = in.read(buf)) >= 0) out.write(buf, 0, n); } }
虽然Object是一个具体的类,但它主要是为继承而设计的。它的所有非 final方法(equals、hashCode、toString、clone和finalize)都有清晰的通用约定( general contracts),因为它们被设计为被子类重写。任何类都有义务重写这些方法,以遵从他们的通用约定;如果不这样做,将会阻止其他依赖于约定的类(例如HashMap和HashSet)与此类一起正常工作。
@Override public boolean equals(Object o) { return o instanceof CaseInsensitiveString && ((CaseInsensitiveString) o).s.equalsIgnoreCase(s); }
传递性(Transitivity)——equals 约定的第三个要求是,如果第一个对象等于第二个对象,第二个对象等于第三个对象,那么第一个对象必须等于第三个对象。同样,也不难想象,无意中违反了这一要求。考虑子类的情况, 将新值组件( value component)添加到其父类中。换句话说,子类添加了一个信息,它影响了equals方法比较。让我们从一个简单不可变的二维整数类型Point类开始:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
public class Point { private final int x; private final int y;
public Point(int x, int y) { this.x = x; this.y = y; }
@Override public boolean equals(Object o) { if (!(o instanceof Point)) return false; Point p = (Point) o; return p.x == x && p.y == y; }
... // Remainder omitted }
假设想继承这个类,将表示颜色的Color类添加到Point类中:
1 2 3 4 5 6 7 8 9 10
public class ColorPoint extends Point { private final Color color;
public ColorPoint(int x, int y, Color color) { super(x, y); this.color = color; }
@Override public boolean equals(Object o) { if (o == null || o.getClass() != getClass()) return false; Point p = (Point) o; return p.x == x && p.y == y; }
// Adds a value component without violating the equals contract public class ColorPoint { private final Point point; private final Color color;
public ColorPoint(int x, int y, Color color) { point = new Point(x, y); this.color = Objects.requireNonNull(color); } /** * Returns the point-view of this color point. */ public Point asPoint() { return point; }
很多 IDE(例如 Eclipse,NetBeans,IntelliJ IDEA 等)也有生成equals和hashCode方法的功能,但是生成的源代码比使用AutoValue框架的代码更冗长、可读性更差,不会自动跟踪类中的更改,因此需要进行测试。这就是说,使用IDE工具生成equals(和hashCode)方法通常比手动编写它们更可取,因为IDE工具不会犯粗心大意的错误,而人类则会。
// Telescoping constructor pattern - does not scale well!
public class NutritionFacts { private final int servingSize; // (mL) required private final int servings; // (per container) required private final int calories; // (per serving) optional private final int fat; // (g/serving) optional private final int sodium; // (mg/serving) optional private final int carbohydrate; // (g/serving) optional
public NutritionFacts(int servingSize, int servings) { this(servingSize, servings, 0); }
public NutritionFacts(int servingSize, int servings, int calories) { this(servingSize, servings, calories, 0); }
public NutritionFacts(int servingSize, int servings, int calories, int fat) { this(servingSize, servings, calories, fat, 0); }
public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium) { this(servingSize, servings, calories, fat, sodium, 0); }
public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium, int carbohydrate) { this.servingSize = servingSize; this.servings = servings; this.calories = calories; this.fat = fat; this.sodium = sodium; this.carbohydrate = carbohydrate; } }
当想要创建一个实例时,可以使用包含所有要设置的参数的最短参数列表的构造方法:
1
NutritionFacts cocaCola = new NutritionFacts(240, 8, 100, 0, 35, 27);
public class NutritionFacts { // Parameters initialized to default values (if any) private int servingSize = -1; // Required; no default value private int servings = -1; // Required; no default value private int calories = 0; private int fat = 0; private int sodium = 0; private int carbohydrate = 0;
public NutritionFacts() { }
// Setters public void setServingSize(int val) { servingSize = val; } public void setServings(int val) { servings = val; } public void setCalories(int val) { calories = val; } public void setFat(int val) { fat = val; } public void setSodium(int val) { sodium = val; } public void setCarbohydrate(int val) { carbohydrate = val; } }
这种模式没有伸缩构造方法模式的缺点。有点冗长,但创建实例很容易,并且易于阅读所生成的代码:
1 2 3 4 5 6
NutritionFacts cocaCola = new NutritionFacts(); cocaCola.setServingSize(240); cocaCola.setServings(8); cocaCola.setCalories(100); cocaCola.setSodium(35); cocaCola.setCarbohydrate(27);
public class NutritionFacts { private final int servingSize; private final int servings; private final int calories; private final int fat; private final int sodium; private final int carbohydrate;
public static class Builder { // Required parameters private final int servingSize; private final int servings;
// Optional parameters - initialized to default values private int calories = 0; private int fat = 0; private int sodium = 0; private int carbohydrate = 0;
public Builder(int servingSize, int servings) { this.servingSize = servingSize; this.servings = servings; }
public abstract class Pizza { public enum Topping {HAM, MUSHROOM, ONION, PEPPER, SAUSAGE} final Set<Topping> toppings; abstract static class Builder<T extends Builder<T>> { EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class);
public T addTopping(Topping topping) { toppings.add(Objects.requireNonNull(topping)); return self(); } abstract Pizza build(); // Subclasses must override this method to return "this" protected abstract T self(); }
public class Calzone extends Pizza { private final boolean sauceInside; public static class Builder extends Pizza.Builder<Builder> { private boolean sauceInside = false; // Default
// Singleton with public final field public class Elvis { public static final Elvis INSTANCE = new Elvis(); private Elvis() { ... } public void leaveTheBuilding() { ... } }
私有构造方法只调用一次,来初始化公共静态 final Elvis.INSTANCE属性。缺少一个公共的或受保护的构造方法,保证了全局的唯一性:一旦Elvis类被初始化,一个Elvis的实例就会存在——不多也不少。客户端所做的任何事情都不能改变这一点,但需要注意的是:特权客户端可以使用AccessibleObject.setAccessible方法,以反射方式调用私有构造方法(条目 65)。如果需要防御此攻击,请修改构造函数,使其在请求创建第二个实例时抛出异常。
在第二个实现单例的方法中,公共成员是一个静态的工厂方法:
1 2 3 4 5 6 7 8
// Singleton with static factory public class Elvis { private static final Elvis INSTANCE = new Elvis(); private Elvis() { ... } public static Elvis getInstance() { return INSTANCE; }
// readResolve method to preserve singleton property private Object readResolve() { // Return the one true Elvis and let the garbage collector // take care of the Elvis impersonator. return INSTANCE; }
实现一个单例的第三种方法是声明单一元素的枚举类:
1 2 3 4 5 6
// Enum singleton - the preferred approach public enum Elvis { INSTANCE;