Effective Java 第三版—— 20. 接口优于抽象类 (2)

按照惯例,骨架实现类被称为AbstractInterface,其中Interface是它们实现的接口的名称。 例如,集合框架( Collections Framework)提供了一个框架实现以配合每个主要集合接口:AbstractCollection,AbstractSet,AbstractList和AbstractMap。 可以说,将它们称为SkeletalCollection,SkeletalSet,SkeletalList和SkeletalMap是有道理的,但是现在已经确立了抽象约定。 如果设计得当,骨架实现(无论是单独的抽象类还是仅由接口上的默认方法组成)可以使程序员非常容易地提供他们自己的接口实现。 例如,下面是一个静态工厂方法,在AbstractList的顶层包含一个完整的功能齐全的List实现:

// Concrete implementation built atop skeletal implementation static List<Integer> intArrayAsList(int[] a) {     Objects.requireNonNull(a);     // The diamond operator is only legal here in Java 9 and later     // If you're using an earlier release, specify <Integer>     return new AbstractList<>() {         @Override public Integer get(int i) {             return a[i];  // Autoboxing ([Item 6](https://www.safaribooksonline.com/library/view/effective-java-third/9780134686097/ch2.xhtml#lev6))         }         @Override public Integer set(int i, Integer val) {             int oldVal = a[I];             a[i] = val;     // Auto-unboxing             return oldVal;  // Autoboxing         }         @Override public int size() {             return a.length;         }     }; }

当你考虑一个List实现为你做的所有事情时,这个例子是一个骨架实现的强大的演示。 顺便说一句,这个例子是一个适配器(Adapter )[Gamma95],它允许一个int数组被看作Integer实例列表。 由于int值和整数实例(装箱和拆箱)之间的来回转换,其性能并不是非常好。 请注意,实现采用匿名类的形式(条目 24)。

骨架实现类的优点在于,它们提供抽象类的所有实现的帮助,而不会强加抽象类作为类型定义时的严格约束。对于具有骨架实现类的接口的大多数实现者来说,继承这个类是显而易见的选择,但它不是必需的。如果一个类不能继承骨架的实现,这个类可以直接实现接口。该类仍然受益于接口本身的任何默认方法。此外,骨架实现类仍然可以协助接口的实现。实现接口的类可以将接口方法的调用转发给继承骨架实现的私有内部类的包含实例。这种被称为模拟多重继承的技术与条目 18讨论的包装类模式密切相关。它提供了多重继承的许多好处,同时避免了缺陷。

编写一个骨架的实现是一个相对简单的过程,虽然有些乏味。 首先,研究接口,并确定哪些方法是基本的,其他方法可以根据它们来实现。 这些基本方法是你的骨架实现类中的抽象方法。 接下来,为所有可以直接在基本方法之上实现的方法提供接口中的默认方法,回想一下,你可能不会为诸如Object类中equals和hashCode等方法提供默认方法。 如果基本方法和默认方法涵盖了接口,那么就完成了,并且不需要骨架实现类。 否则,编写一个声明实现接口的类,并实现所有剩下的接口方法。 为了适合于该任务,此类可能包含任何的非公共属性和方法。

作为一个简单的例子,考虑一下Map.Entry接口。 显而易见的基本方法是getKey,getValue和(可选的)setValue。 接口指定了equals和hashCode的行为,并且在基本方面方面有一个toString的明显的实现。 由于不允许为Object类方法提供默认实现,因此所有实现均放置在骨架实现类中:

// Skeletal implementation class public abstract class AbstractMapEntry<K,V>         implements Map.Entry<K,V> {     // Entries in a modifiable map must override this method     @Override 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<?,?> e = (Map.Entry) o;         return Objects.equals(e.getKey(),  getKey())             && Objects.equals(e.getValue(), getValue());     }     // Implements the general contract of Map.Entry.hashCode     @Override public int hashCode() {         return Objects.hashCode(getKey())              ^ Objects.hashCode(getValue());     }     @Override public String toString() {         return getKey() + "=" + getValue();     } }

请注意,这个骨架实现不能在Map.Entry接口中实现,也不能作为子接口实现,因为默认方法不允许重写诸如equals,hashCode和toString等Object类方法。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpdzwp.html