mirror of
https://github.com/Jueee/effective-Java.git
synced 2026-08-23 10:33:28 +08:00
接口优于抽象类
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
## 接口优于抽象类
|
||||
|
||||
Java 有两种机制来定义允许多个实现的类型:**接口** 和 **抽象类**。
|
||||
Java 有两种机制来定义允许多个实现的类型:**接口** 和 **抽象类**。
|
||||
|
||||
由于在 Java 8 中引入了接口的默认方法(default methods ),因此这两种机制都允许为某些实例方法提供实现。 一个主要的区别是要实现由抽象类定义的类型,类必须是抽象类的子类。 因为 Java 只允许单一继承,所以对抽象类的这种限制严格限制了它们作为类型定义的使用。 任何定义所有必需方法并服从通用约定的类都可以实现一个接口,而不管类在类层次结构中的位置。
|
||||
|
||||
@@ -14,3 +14,22 @@ Java 有两种机制来定义允许多个实现的类型:**接口** 和 **抽
|
||||
|
||||
**示例代码**:[Item20Example01.java](ClassesAndInterfaces/src/main/java/com/jueee/item20/Item20Example01.java):定义一个继承歌手和作曲家的接口,并添加适合于这个组合的新方法。
|
||||
|
||||
#### 骨架实现类
|
||||
|
||||
可以通过提供一个抽象的骨架实现类(abstract skeletal implementation class)来与接口一起使用,将接口和抽象类的优点结合起来。
|
||||
|
||||
接口定义了类型,可能提供了一些默认的方法,而骨架实现类在原始接口方法的顶层实现了剩余的非原始接口方法。 继承骨架实现需要大部分的工作来实现一个接口。 这就是模板方法设计模式。
|
||||
|
||||
**示例代码**:[Item20Example02.java](ClassesAndInterfaces/src/main/java/com/jueee/item20/Item20Example02.java):在 `AbstractList` 的顶层包含一个完整的功能齐全的 `List` 实现。
|
||||
|
||||
骨架实现类的优点在于,它们提供抽象类的所有实现的帮助,而不会强加抽象类作为类型定义时的严格约束。对于具有骨架实现类的接口的大多数实现者来说,继承这个类是显而易见的选择,但它不是必需的。如果一个类不能继承骨架的实现,这个类可以直接实现接口。该类仍然受益于接口本身的任何默认方法。此外,骨架实现类仍然可以协助接口的实现。实现接口的类可以将接口方法的调用转发给继承骨架实现的私有内部类的包含实例。这种被称为模拟多重继承的技术与条目 18 讨论的包装类模式密切相关。它提供了多重继承的许多好处,同时避免了缺陷。
|
||||
|
||||
编写一个骨架的实现是一个相对简单的过程,虽然有些乏味。 首先,研究接口,并确定哪些方法是基本的,其他方法可以根据它们来实现。 这些基本方法是你的骨架实现类中的抽象方法。 接下来,为所有可以直接在基本方法之上实现的方法提供接口中的默认方法,回想一下,你可能不会为诸如 `Object` 类中 `equals` 和 `hashCode` 等方法提供默认方法。 如果基本方法和默认方法涵盖了接口,那么就完成了,并且不需要骨架实现类。 否则,编写一个声明实现接口的类,并实现所有剩下的接口方法。 为了适合于该任务,此类可能包含任何的非公共属性和方法。
|
||||
|
||||
**示例代码**:[Item20Example03.java](ClassesAndInterfaces/src/main/java/com/jueee/item20/Item20Example03.java): 在骨架实现类中实现 `Map.Entry` 接口。
|
||||
|
||||
由于骨架实现类是为了继承而设计的,所以你应该遵循条目 19 中的所有设计和文档说明。为了简洁起见,前面的例子中省略了文档注释,但是好的文档在骨架实现中是绝对必要的,无论它是否包含 一个接口或一个单独的抽象类的默认方法。
|
||||
|
||||
与骨架实现有稍许不同的是简单实现,以 `AbstractMap.SimpleEntry` 为例。 一个简单的实现就像一个骨架实现,它实现了一个接口,并且是为了继承而设计的,但是它的不同之处在于它不是抽象的:它是最简单的工作实现。 你可以按照情况使用它,也可以根据情况进行子类化。
|
||||
|
||||
总而言之,一个接口通常是定义允许多个实现的类型的最佳方式。 如果你导出一个重要的接口,应该强烈考虑提供一个骨架的实现类。 在可能的情况下,应该通过接口上的默认方法提供骨架实现,以便接口的所有实现者都可以使用它。 也就是说,对接口的限制通常要求骨架实现类采用抽象类的形式。
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.jueee.item20;
|
||||
|
||||
import java.util.AbstractList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class Item20Example02 {
|
||||
|
||||
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<Integer>() {
|
||||
@Override
|
||||
public Integer get(int i) {
|
||||
return a[i]; // Autoboxing
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.jueee.item20;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public class Item20Example03 {
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user