Java语言设计提供了两种机制,可以用来定义允许多个实现的类型:接口和抽象类。
// Concrete implementation built atop skeletal implementation - Page 95 import java.util.*; public class IntArrays { static List<Integer> intArrayAsList(final int[] a) { if (a == null) throw new NullPointerException(); return new AbstractList<Integer>() { public Integer get(int i) { return a[i]; // Autoboxing (Item 5) } @Override public Integer set(int i, Integer val) { int oldVal = a[i]; a[i] = val; // Auto-unboxing return oldVal; // Autoboxing } public int size() { return a.length; } }; } public static void main(String[] args) { int[] a = new int[10]; for (int i = 0; i < a.length; i++) a[i] = i; List<Integer> list = intArrayAsList(a); Collections.shuffle(list); System.out.println(list); } }
因为Java只允许单继承,所以抽象类作为类型定义受到了极大的限制。
现有的类可以很容易被更新,以实现新的接口。
一般来说,无法更新现有的类来扩展新的抽象类。如果你希望两个类来扩展一个抽象类,就必须把抽象类放到类型层次的高处,以便这两个类的一个祖先成为他的子类。这样会间接的伤害到类的层次,迫使这个公共祖先的所有后代类都扩展这个新的抽象类,无论他对于这个后代类是否合适。
接口是定义mixin(混合类型)的理想选择。类除了实现他的“基本类型(primary type)”之外,还可以实现这个mixin类型,以表示提供了某些可供选择的行为
接口允许我们构造非层次结构的类型框架。包装类(wrapper class)模式,接口使得安全地增强类的功能成为可能。
通过对你导出的每个重要接口都提供一个抽象的骨架实现(skeletal implementation)类,把这个抽象类的优点结合起来。必须认真研究接口,并确定哪些方法是最为基本的(primitive),其他的方法则可以根据他们来实现。这些基本的方法将成为骨架实现类中抽象方法。然后,必须为接口中所有其他的方法提供具体的实现。
抽象类的演变比接口的演变要容易的多。接口一旦被公开发行,并且已被广泛实现,再想改变这个接口几乎是不可能的。
// Skeletal Implementation - Page 96 import java.util.*; public abstract class AbstractMapEntry<K,V> implements Map.Entry<K,V> { // Primitive operations public abstract K getKey(); public abstract V getValue(); // Entries in modifiable maps must override this method public V setValue(V value) { throw new UnsupportedOperationException(); } // Implements the general contract of Map.Entry.equals @Override public boolean equals(Object o) { if (o == this) return true; if (! (o instanceof Map.Entry)) return false; Map.Entry<?,?> arg = (Map.Entry) o; return equals(getKey(), arg.getKey()) && equals(getValue(), arg.getValue()); } private static boolean equals(Object o1, Object o2) { return o1 == null ? o2 == null : o1.equals(o2); } // Implements the general contract of Map.Entry.hashCode @Override public int hashCode() { return hashCode(getKey()) ^ hashCode(getValue()); } private static int hashCode(Object obj) { return obj == null ? 0 : obj.hashCode(); } }// Concrete implementation built atop skeletal implementation - Page 95 import java.util.*; public class IntArrays { static List<Integer> intArrayAsList(final int[] a) { if (a == null) throw new NullPointerException(); return new AbstractList<Integer>() { public Integer get(int i) { return a[i]; // Autoboxing (Item 5) } @Override public Integer set(int i, Integer val) { int oldVal = a[i]; a[i] = val; // Auto-unboxing return oldVal; // Autoboxing } public int size() { return a.length; } }; } public static void main(String[] args) { int[] a = new int[10]; for (int i = 0; i < a.length; i++) a[i] = i; List<Integer> list = intArrayAsList(a); Collections.shuffle(list); System.out.println(list); } }