抽象方法
- Java 提供了一个叫做 抽象方法 的机制,这个方法是不完整的:它只有声明没有方法体。下面是抽象方法的声明语法。
- 包含抽象方法的类叫做 抽象类 。
- 如果 一个类包含一个或多个抽象方法,那么类本身也必须限定为抽象的
- 无法创建一个抽象方法的对象
- 不存在 private abstrac写法
abstract class Basic {
abstract void unimplemented();
}
abstract class Abs {
int a = 1;
int f() {
return 111;
}
abstract void xx();
}
abstract class Basic {
abstract void unimplemented();
}
abstract class Abs {
int a = 1;
int f() {
return 111;
}
abstract void xx();
}
接口
描述 Java 8 之前的接口更加容易,因为它们只允许抽象方法。像下面这样:
public interface PureInterface {
int m1();
void m2();
double m3();
}
public interface PureInterface {
int m1();
void m2();
double m3();
}
在 Java 8 之前我们可以这么说:interface 关键字产生一个完全抽象的类,没有提供任何实现。
Java 8 中接口稍微有些变化,因为 Java 8 允许接口包含默认方法和静态方法——基于某些重要原因,看到后面你会理解。接口的基本概念仍然没变,介于类型之上、实现之下。接口与抽象类最明显的区别可能就是使用上的惯用方式。接口的典型使用是代表一个类的类型或一个形容词,如 Runnable 或 Serializable,而抽象类通常是类层次结构的一部分或一件事物的类型,如 String 或 ActionHero。
增加默认方法的极具说服力的理由是它允许在不破坏已使用接口的代码的情况下,在接口中增加新的方法。
默认方法有时也被称为 守卫方法 或 虚拟扩展方法。
- 接口名和java文件名相同时,需要用public修饰(当然)
- 接口同样可以包含属性,这些属性被隐式指明为 static 和 final
默认方法
Java 8 为关键字 default 增加了一个新的用途(之前只用于 switch 语句和注解中)
interface InterfaceWithDefault {
void firstMethod();
void secondMethod();
default void newMethod() {
System.out.println("newMethod");
}
}
public class Implementation2 implements InterfaceWithDefault {
@Override
public void firstMethod() {
System.out.println("firstMethod");
}
@Override
public void secondMethod() {
System.out.println("secondMethod")
}
public static void main(String[] args) {
InterfaceWithDefault i = new Implementation2();
i.firstMethod();
i.secondMethod();
i.newMethod();
}
}
interface InterfaceWithDefault {
void firstMethod();
void secondMethod();
default void newMethod() {
System.out.println("newMethod");
}
}
public class Implementation2 implements InterfaceWithDefault {
@Override
public void firstMethod() {
System.out.println("firstMethod");
}
@Override
public void secondMethod() {
System.out.println("secondMethod")
}
public static void main(String[] args) {
InterfaceWithDefault i = new Implementation2();
i.firstMethod();
i.secondMethod();
i.newMethod();
}
}
多继承
- 在 Java 8 之前,接口没有包袱——它只是方法外貌的描述
- 多年后的现在,Java 通过默认方法具有了某种多继承的特性。结合带有默认方法的接口意味着结合了多个基类中的行为。因为接口中仍然不允许存在属性(只有静态属性,不适用),所以属性仍然只会来自单个基类或抽象类,也就是说,不会存在状态的多继承。正如下面这样:
import java.util.*;
interface One {
default void first() {
System.out.println("first");
}
}
interface Two {
default void second() {
System.out.println("second");
}
}
interface Three {
default void third() {
System.out.println("third");
}
}
class MI implements One, Two, Three {}
public class MultipleInheritance {
public static void main(String[] args) {
MI mi = new MI();
mi.first();
mi.second();
mi.third();
}
}
import java.util.*;
interface One {
default void first() {
System.out.println("first");
}
}
interface Two {
default void second() {
System.out.println("second");
}
}
interface Three {
default void third() {
System.out.println("third");
}
}
class MI implements One, Two, Three {}
public class MultipleInheritance {
public static void main(String[] args) {
MI mi = new MI();
mi.first();
mi.second();
mi.third();
}
}
抽象类和接口 区分
使用继承扩展接口
通过继承,可以很容易在接口中增加方法声明,还可以在新接口中结合多个接口。这两种情况都可以得到新接口,如下例所示:
interface Monster {
void menace();
}
interface DangerousMonster extends Monster {
void destroy();
}
interface Lethal {
void kill();
}
interface Vampire extends DangerousMonster, Lethal {
void drinkBlood();
}
//--------
class DragonZilla implements DangerousMonster {
@Override
public void menace() {}
@Override
public void destroy() {}
}
class VeryBadVampire implements Vampire {
@Override
public void menace() {}
@Override
public void destroy() {}
@Override
public void kill() {}
@Override
public void drinkBlood() {}
}
public class HorrorShow {
static void u(Monster b) {
b.menace();
}
static void v(DangerousMonster d) {
d.menace();
d.destroy();
}
static void w(Lethal l) {
l.kill();
}
public static void main(String[] args) {
DangerousMonster barney = new DragonZilla();
u(barney);
v(barney);
Vampire vlad = new VeryBadVampire();
u(vlad);
v(vlad);
w(vlad);
}
}
interface Monster {
void menace();
}
interface DangerousMonster extends Monster {
void destroy();
}
interface Lethal {
void kill();
}
interface Vampire extends DangerousMonster, Lethal {
void drinkBlood();
}
//--------
class DragonZilla implements DangerousMonster {
@Override
public void menace() {}
@Override
public void destroy() {}
}
class VeryBadVampire implements Vampire {
@Override
public void menace() {}
@Override
public void destroy() {}
@Override
public void kill() {}
@Override
public void drinkBlood() {}
}
public class HorrorShow {
static void u(Monster b) {
b.menace();
}
static void v(DangerousMonster d) {
d.menace();
d.destroy();
}
static void w(Lethal l) {
l.kill();
}
public static void main(String[] args) {
DangerousMonster barney = new DragonZilla();
u(barney);
v(barney);
Vampire vlad = new VeryBadVampire();
u(vlad);
v(vlad);
w(vlad);
}
}
接口嵌套
接口可以嵌套在类或其他接口中。
interface E {
interface G {
void f();
}
public interface H {
void f();
}
void g();
}
class A {
interface B {
void f();
}
public class BImp implements B {
@Override
public void f() {}
}
}
interface E {
interface G {
void f();
}
public interface H {
void f();
}
void g();
}
class A {
interface B {
void f();
}
public class BImp implements B {
@Override
public void f() {}
}
}
接口和工厂方法模式
接口是多实现的途径,而生成符合某个接口的对象的典型方式是 工厂方法 设计模式。
interface Service {
void method1();
void method2();
}
class Service1 implements Service {
Service1() {
}
@Override
public void method1() {
System.out.println("Service1 method1");
}
@Override
public void method2() {
System.out.println("Service1 method2");
}
}
class Service2 implements Service {
Service2() {
}
@Override
public void method1() {
System.out.println("Service2 method1");
}
@Override
public void method2() {
System.out.println("Service2 method2");
}
}
interface Service {
void method1();
void method2();
}
class Service1 implements Service {
Service1() {
}
@Override
public void method1() {
System.out.println("Service1 method1");
}
@Override
public void method2() {
System.out.println("Service1 method2");
}
}
class Service2 implements Service {
Service2() {
}
@Override
public void method1() {
System.out.println("Service2 method1");
}
@Override
public void method2() {
System.out.println("Service2 method2");
}
}
factory:
interface ServiceFactory {
Service getService();
}
class Service1Factory implements ServiceFactory {
@Override
public Service getService() {
return new Service1();
}
}
class Service2Factory implements ServiceFactory {
@Override
public Service getService() {
return new Service2();
}
}
--- 调用
public class Main {
public static void serviceConsumer(ServiceFactory fact) {
Service s = fact.getService();
s.method1();
s.method2();
}
public static void main(String[] args) {
serviceConsumer(new Service1Factory());
serviceConsumer(new Service2Factory());
}
}
interface ServiceFactory {
Service getService();
}
class Service1Factory implements ServiceFactory {
@Override
public Service getService() {
return new Service1();
}
}
class Service2Factory implements ServiceFactory {
@Override
public Service getService() {
return new Service2();
}
}
--- 调用
public class Main {
public static void serviceConsumer(ServiceFactory fact) {
Service s = fact.getService();
s.method1();
s.method2();
}
public static void main(String[] args) {
serviceConsumer(new Service1Factory());
serviceConsumer(new Service2Factory());
}
}
如果没有工厂方法,代码就必须在某处指定将要创建的 Service 的确切类型,从而调用恰当的构造器。
为什么要添加额外的间接层呢?一个常见的原因是创建框架。复用复杂的代码。
本章小结
认为接口是好的选择,从而使用接口不用具体类,这具有诱惑性。几乎任何时候,创建类都可以替代为创建一个接口和工厂。
很多人都掉进了这个陷阱,只要有可能就创建接口和工厂。这种逻辑看起来像是可能会使用不同的实现,所以总是添加这种抽象性。这变成了一种过早的设计优化。
任何抽象性都应该是由真正的需求驱动的。当有必要时才应该使用接口进行重构,而不是到处添加额外的间接层,从而带来额外的复杂性。这种复杂性非常显著,如果你让某人去处理这种复杂性,只是因为你意识到 “以防万一” 而添加新接口,而没有其他具有说服力的原因——好吧,如果我碰上了这种设计,就会质疑此人所作的所有其他设计了。
恰当的原则是优先使用类而不是接口。从类开始,如果使用接口的必要性变得很明确,那么就重构。接口是一个伟大的工具,但它们容易被滥用。