Thinking in Java 第四版 17.4章节中提到了可选操作。其中典型的例子之一就是Arrays.asList(),直接进来看一下是怎么回事。
1 //java.util.Arrays 2 /** 3 * Returns a fixed-size list backed by the specified array. (Changes to 4 * the returned list "write through" to the array.) This method acts 5 * as bridge between array-based and collection-based APIs, in 6 * combination with {@link Collection#toArray}. The returned list is 7 * serializable and implements {@link RandomAccess}. 8 * 9 * <p>This method also provides a convenient way to create a fixed-size 10 * list initialized to contain several elements: 11 * <pre> 12 * List<String> stooges = Arrays.asList("Larry", "Moe", "Curly"); 13 * </pre> 14 * 15 * @param <T> the class of the objects in the array 16 * @param a the array by which the list will be backed 17 * @return a list view of the specified array 18 */ 19 @SafeVarargs 20 @SuppressWarnings("varargs") 21 public static <T> List<T> asList(T... a) { 22 return new ArrayList<>(a); 23 }
可以看到,方法直接返回一个ArrayList,但此ArrayList非彼ArrayList,这个ArrayList是Arrays里的私有静态内部类:
1 /** 2 * @serial include 3 */ 4 private static class ArrayList<E> extends AbstractList<E> 5 implements RandomAccess, java.io.Serializable 6 { 7 private static final long serialVersionUID = -2764017481108945198L; 8 private final E[] a; 9 10 ArrayList(E[] array) { 11 a = Objects.requireNonNull(array); 12 } 13 14 @Override 15 public int size() { 16 return a.length; 17 } 18 19 @Override 20 public Object[] toArray() { 21 return a.clone(); 22 } 23 24 @Override 25 @SuppressWarnings("unchecked") 26 public <T> T[] toArray(T[] a) { 27 int size = size(); 28 if (a.length < size) 29 return Arrays.copyOf(this.a, size, 30 (Class<? extends T[]>) a.getClass()); 31 System.arraycopy(this.a, 0, a, 0, size); 32 if (a.length > size) 33 a[size] = null; 34 return a; 35 } 36 37 @Override 38 public E get(int index) { 39 return a[index]; 40 } 41 42 @Override 43 public E set(int index, E element) { 44 E oldValue = a[index]; 45 a[index] = element; 46 return oldValue; 47 } 48 49 @Override 50 public int indexOf(Object o) { 51 E[] a = this.a; 52 if (o == null) { 53 for (int i = 0; i < a.length; i++) 54 if (a[i] == null) 55 return i; 56 } else { 57 for (int i = 0; i < a.length; i++) 58 if (o.equals(a[i])) 59 return i; 60 } 61 return -1; 62 } 63 64 @Override 65 public boolean contains(Object o) { 66 return indexOf(o) != -1; 67 } 68 69 @Override 70 public Spliterator<E> spliterator() { 71 return Spliterators.spliterator(a, Spliterator.ORDERED); 72 } 73 74 @Override 75 public void forEach(Consumer<? super E> action) { 76 Objects.requireNonNull(action); 77 for (E e : a) { 78 action.accept(e); 79 } 80 } 81 82 @Override 83 public void replaceAll(UnaryOperator<E> operator) { 84 Objects.requireNonNull(operator); 85 E[] a = this.a; 86 for (int i = 0; i < a.length; i++) { 87 a[i] = operator.apply(a[i]); 88 } 89 } 90 91 @Override 92 public void sort(Comparator<? super E> c) { 93 Arrays.sort(a, c); 94 } 95 }
这里的确没有add等方法,首先接口里声明的方法是一定要实现的,既然不在这里那就去它的父类,AbstractList,里面寻找答案。
1 // java.util.AbstractList 2 public boolean add(E e) { 3 add(size(), e); 4 return true; 5 } 6 7 8 public void add(int index, E element) { 9 throw new UnsupportedOperationException(); 10 }
很明显,到这里就清楚了,在这个抽象类里提供了add方法的实现——抛出UnsupportedOperationException异常。而在Arrays里返回的内部类ArrayList由于没有提供add方法的自己实现,于是你在返回的ArrayList上调用add方法实际上就是AbstractList里提供的版本。其实更直接地,可以从报错信息里看到:
Exception in thread "main" java.lang.UnsupportedOperationException at java.util.AbstractList.add(AbstractList.java:148) at java.util.AbstractList.add(AbstractList.java:108)
就是调用了AbstractList类的add方法,而它则“只有在运行时才会探测到”。另外,在AbstractList中set()和remove()方法都是通过这种方式实现的,“而读取方法都不是可选的”。
这就是所谓可选操作,其原因书里有解释:
“这样做可以防止在设计中出现接口爆炸的情况”。通过抽象类来推迟某些方法的实现,直到,需要实现的时候。
浙公网安备 33010602011771号