接口和抽象类提供了一种将接口与实现分离的更加结构化的方法。
抽象类和方法
包含抽象方法的类叫做抽象类。如果一个类包含一个或多个抽象方法,那么类本身也必须限定为抽象的,否则,编译器会报错。
可以将一个不包含任何抽象方法的类指明为 abstract,阻止创建类的对象。
抽象方法默认为包访问权限。
接口创建
一个接口表示:所有实现了该接口的类看起来都像这样。因此,任何使用某特定接口的代码都知道可以调用该接口的哪些方法,而且仅需知道这些。所以,接口被用来建立类之间的协议。
Java 8 中接口稍微有些变化,因为 Java 8 允许接口包含默认方法和静态方法。
接口与抽象类最明显的区别可能就是使用上的惯用方式。接口的典型使用是代表一个类的类型或一个形容词,如 Runnable 或 Serializable,而抽象类通常是类层次结构的一部分或一件事物的类型,如 String 或 ActionHero。
接口同样可以包含属性,这些属性被隐式指明为 static 和 final。
接口中的方法默认为 public
。
默认方法
关键字default
允许在接口中提供方法实现——在 Java 8 之前被禁止。
增加默认方法的极具说服力的理由是它允许在不破坏已使用接口的代码的情况下,在接口中增加新的方法。
多继承
当实现的两个接口中有相同的默认实现方法签名时,编译器会报错。可以通过覆写冲突的方法。
interface Jim1 {
default void jim() {
System.out.println("Jim1::jim");
}
}
interface Jim2 {
default void jim() {
System.out.println("Jim2::jim");
}
}
public class Jim implements Jim1, Jim2 {
@Override
public void jim() {
Jim2.super.jim();
}
public static void main(String[] args) {
new Jim().jim();
}
}
接口中的静态方法
Java 8 允许在接口中添加静态方法。这么做能恰当地把工具功能置于接口中,从而操作接口,或者成为通用的工具。
抽象类和接口
特性 | 接口 | 抽象类 |
---|---|---|
组合 | 新类可以组合多个接口 | 只能继承单一抽象类 |
状态 | 不能包含属性(除了静态属性,不支持对象状态) | 可以包含属性,非抽象方法可能引用这些属性 |
默认方法 和 抽象方法 | 不需要在子类中实现默认方法。默认方法可以引用其他接口的方法 | 必须在子类中实现抽象方法 |
构造器 | 没有构造器 | 可以有构造器 |
可见性 | 隐式 public | 可以是 protected 或包访问权限 |
有一条实际经验:尽可能地抽象。因此,更倾向使用接口而不是抽象类。只有当必要时才使用抽象类。除非必须使用,否则不要用接口和抽象类。大多数时候,普通类已经做得很好,如果不行的话,再移动到接口或抽象类中。
完全解耦
可以在接口中定义 main() 方法。
多接口结合
可以有任意多个接口,并可以向上转型为每个接口,因为每个接口都是独立的类型。
使用接口的核心原因之一:为了能够向上转型为多个基类型。
使用接口的第二个原因与使用抽象基类相同:防止客户端程序员创建这个类的对象,确保这仅仅只是一个接口。
如果创建不带任何方法定义或成员变量的基类,就选择接口而不是抽象类。事实上,如果知道某事物是一个基类,可以考虑用接口实现它。
使用继承扩展接口
interface Vampire extends DangerousMonster, Lethal {
void drinkBlood();
}
接口字段
因为接口中的字段都自动是 static 和 final 的,所以接口就成为了创建一组常量的方便的工具。自 Java 5 开始,我们有了更加强大和灵活的关键字 enum
,那么在接口中定义常量组就显得没什么意义了。
这些字段不是接口的一部分,它们的值被存储在接口的静态存储区域中。
接口嵌套
接口可以嵌套在类或其他接口中。
嵌套在类中的接口可以是private的。
实现private 嵌套接口是一种可以强制该接口中的方法定义不会添加任何类型信息(即不可以向上转型)的方式。
嵌套在另一个接口中的接口自动就是 public 的,不能指明为 private。
接口和工厂方法模式
接口是多实现的途径,而生成符合某个接口的对象的典型方式是工厂方法设计模式。
本章小结
恰当的原则是优先使用类而不是接口。从类开始,如果使用接口的必要性变得很明确,那么就重构。接口是一个伟大的工具,但它们容易被滥用。