重温经典《Thinking in java》第四版之第九章 接口(四十六)

简介: 重温经典《Thinking in java》第四版之第九章 接口(四十六)

接口和内部类为我们提供了一种将接口与实现分离的更加结构化的方法。

这种机制在编程语言中并不通用。例如,C++对这些概念只有间接的支持。Java中存在语言关键字这个事实表明人们认为这些思想是很重要的,以至于要提供对它们的直接支持。

首先我们将学习抽象类,它是普通类与接口之间的一种中庸之道。尽管构建具有某些未实现方法的类时,你的第一想法可能是创建接口,但是抽象类仍旧是用于此目的的一种重要而必须的工具,因为你不可能总是使用纯接口。

 

9.1 抽象类和抽象方法

Java提供一个叫做抽象方法的机制,这种方法是不完整的:仅有声明而没有方法体。下面是抽象方法声明所采用的语法:

abstract void f( );

包含抽象方法的类叫做抽象类。如果一个类包含一个或者多个抽象方法,该类必须被限定为抽象的。(否则,编译器就会报错。)

如果一个抽象类不完整,那么当我们试图产生该类的对象时,编译器会怎样处理呢?由于为抽象类创建对象时不安全的,所以我们会从编译器那里得到一条出错消息。这样,编译器会确保抽象类的纯粹性,我们不必担心误用它。

如果从一个抽象类继承,并想创建该新类的对象,那么必须为基类中的所有抽象方法提供方法定义。如果不这样做(可以选择不做),那么导出类便也是抽象类,且编译器将会强制我们用abstract关键字来限定这个类、

我们也可能会创建一个没有任何抽象方法的抽象类。考虑这种情况:如果有一个了,让其包含任何abstract方法方法都显得没有实际意义,而且我们也想要阻止产生这个类的任何对象,那么这时这样做就很有用了。

即使使某个非抽象类成为抽象类并不需要所有的方法都是抽象的,所以仅需将某些方法声明为抽象的即可。如下图所示:

image.png

下面是修改过的“管弦乐器”的例子,其中采用了抽象类和抽象方法:

//: interfaces/music4/Music4.java // Abstract classes and methods. packageinterfaces.music4; 
importpolymorphism.music.Note; 
importstaticnet.mindview.util.Print.*; 
abstractclassInstrument { 
privateinti; // Storage allocated for each publicabstractvoidplay(Noten); 
publicStringwhat() { return"Instrument"; } 
publicabstractvoidadjust(); 
} 
classWindextendsInstrument { 
publicvoidplay(Noten) { 
print("Wind.play() "+n); 
    } 
publicStringwhat() { return"Wind"; } 
publicvoidadjust() {} 
} 
classPercussionextendsInstrument { 
publicvoidplay(Noten) { 
print("Percussion.play() "+n); 
    } 
publicStringwhat() { return"Percussion"; } 
publicvoidadjust() {} 
} 
classStringedextendsInstrument { 
publicvoidplay(Noten) { 
print("Stringed.play() "+n); 
    } 
publicStringwhat() { return"Stringed"; } 
publicvoidadjust() {} 
} 
classBrassextendsWind { 
publicvoidplay(Noten) { 
print("Brass.play() "+n); 
    } 
publicvoidadjust() { print("Brass.adjust()"); } 
} 
classWoodwindextendsWind { 
publicvoidplay(Noten) { 
print("Woodwind.play() "+n); 
    } 
publicStringwhat() { return"Woodwind"; } 
} 
publicclassMusic4 { 
// Doesn’t care about type, so new types // added to the system still work right: staticvoidtune(Instrumenti) { 
// ... i.play(Note.MIDDLE_C); 
    } 
staticvoidtuneAll(Instrument[] e) { 
for(Instrumenti : e) 
tune(i); 
    } 
publicstaticvoidmain(String[] args) { 
// Upcasting during addition to the array: Instrument[] orchestra= { 
newWind(), 
newPercussion(), 
newStringed(), 
newBrass(), 
newWoodwind() 
        }; 
tuneAll(orchestra); 
    } 
}

/* Output:

Wind.play() MIDDLE_C

Percussion.play() MIDDLE_C

Stringed.play() MIDDLE_C

Brass.play() MIDDLE_C

Woodwind.play() MIDDLE_C

*///:~

我们看到除了基类,实际上并没有什么改变。

创建抽象类和抽象方法非常有用,因为他们可以使类的抽象性明确起来,并告诉用户和编译器打算怎样来使用它们。抽象类还是很有用的重构工具,因为它们使得我们可以很容易地将公共方法沿着继承层次结构向上移动。

 

9.2 接口

interface关键字使抽象的概念更向前迈进了一步。abstract关键字允许人们在类中创建一个或者多个没有任何定义的方法——提供了接口部分,但是没有提供任何相应的具体实现,这些实现是由此类的继承者创建的。interface这个关键字产生一个完全抽象的类,它根本就没有提供任何具体的实现(新版本的JDK允许接口拥有默认实现)。它允许创建者确定方法名、参数列表和返回类型,但是没有任何方法体。接口只提供形式,而为提供任何具体的实现。

一个接口表示:“所有实现了该特定接口的类看起来都像这样”。因此,任何使用某特定接口的代码都知道可以调用该接口的哪些方法,而且仅需知道这些。因此,接口被用来建立类与类之间的协议。(某些面向对象编程语言使用关键字protocol来完成这一功能。)

但是,interface不仅仅是一个极度抽象的类,因为它允许人们通过创建一个能够被相送转型为多种基类的类型,来实现某种类似多重继承的特性。

要想创建一个接口,需要使用interface关键字来替代class关键字。就像一个类一样,可以在interface关键字前面添加public关键字(但仅限于该接口在与其同名的文件中被定义)。如果不添加public关键字,则它只具有包访问权限,这样它就只能在同一个包内可用。接口也可以包含域,但是这些域隐式地是static和final的。

要让一个类遵循某个特定接口或者一组接口,需要使用implements关键字,它表示:“interface只是它的外貌,但是现在我要声明它是如何工作的。”除此以外,它看起来还很像继承。“乐器”示例的图说明了这一点:

image.png

可以从WoodwindBrass类中看到,一旦实现了某个接口,其实现就变成了一个普通的类,就可以按常规方式扩展它。

可以选择在接口中显式地将方法声明为public的,但即使你不这么做,它们也是public的。因此,当要实现一个接口时,在接口中被定义的方法必须被定义为public的;否则它们将只能得到默认的包访问权限,这样在方法被继承的过程中,其可访问权限就被降低了,这是Java编译器所不允许的。

//: interfaces/music5/Music5.java // Interfaces. packageinterfaces.music5; 
importpolymorphism.music.Note; 
importstaticnet.mindview.util.Print.*; 
interfaceInstrument { 
// Compile-time constant: intVALUE=5; // static & final // Cannot have method definitions: voidplay(Noten); // Automatically public voidadjust(); 
} 
classWindimplementsInstrument { 
publicvoidplay(Noten) { 
print(this+".play() "+n); 
    } 
publicStringtoString() { return"Wind"; } 
publicvoidadjust() { print(this+".adjust()"); } 
} 
classPercussionimplementsInstrument { 
publicvoidplay(Noten) { 
print(this+".play() "+n); 
    } 
publicStringtoString() { return"Percussion"; } 
publicvoidadjust() { print(this+".adjust()"); } 
} 
classStringedimplementsInstrument { 
publicvoidplay(Noten) { 
print(this+".play() "+n); 
    } 
publicStringtoString() { return"Stringed"; } 
publicvoidadjust() { print(this+".adjust()"); } 
} 
classBrassextendsWind { 
publicStringtoString() { return"Brass"; } 
} 
classWoodwindextendsWind { 
publicStringtoString() { return"Woodwind"; } 
} 
publicclassMusic5 { 
// Doesn’t care about type, so new types // added to the system still work right: staticvoidtune(Instrumenti) { 
// ... i.play(Note.MIDDLE_C); 
    } 
staticvoidtuneAll(Instrument[] e) { 
for(Instrumenti : e) 
tune(i); 
    } 
publicstaticvoidmain(String[] args) { 
// Upcasting during addition to the array: Instrument[] orchestra= { 
newWind(), 
newPercussion(), 
newStringed(), 
newBrass(), 
newWoodwind() 
        }; 
tuneAll(orchestra); 
    } 
}

/* Output:

Wind.play() MIDDLE_C

Percussion.play() MIDDLE_C

Stringed.play() MIDDLE_C

Brass.play() MIDDLE_C

Woodwind.play() MIDDLE_C

*///:~

目录
相关文章
|
1天前
|
NoSQL Java API
java一行代码实现RESTFul接口
Spring Data REST是构建在Spring Data之上的库,可自动将repository转换为REST服务,支持JPA、MongoDB、Neo4j、GemFire和Cassandra。无需手动创建Service和Controller层。要开始,需配置JPA数据源,创建实体类和Repository接口。快速实现REST接口,只需引入spring-boot-starter-data-rest Maven依赖,并在Repository接口上添加@RepositoryRestResource注解。
|
2天前
|
Java 程序员 数据格式
关于Java抽象类和接口的总结和一点个人的看法
关于Java抽象类和接口的总结和一点个人的看法
|
7天前
|
存储 安全 Java
[Java基础面试题] Map 接口相关
[Java基础面试题] Map 接口相关
|
8天前
|
Java
一文搞清楚Java中的包、类、接口
包、类、接口、方法、变量、参数、代码块,这些都是构成Java程序的核心部分,即便最简单的一段代码里都至少要包含里面的三四个内容,这两天花点时间梳理了一下,理解又深刻了几分。
31 10
|
12天前
|
Java 开发者
探索 Java 的函数式接口和 Lambda 表达式
【4月更文挑战第19天】Java 中的函数式接口和 Lambda 表达式提供了简洁、灵活的编程方式。函数式接口有且仅有一个抽象方法,用于与 Lambda(一种匿名函数语法)配合,简化代码并增强可读性。Lambda 表达式的优点在于其简洁性和灵活性,常用于事件处理、过滤和排序等场景。使用时注意兼容性和变量作用域,它们能提高代码效率和可维护性。
|
13天前
|
Java
Java接口中可以定义哪些方法?
【4月更文挑战第13天】
14 0
Java接口中可以定义哪些方法?
|
13天前
|
Java API
什么是Java函数式接口?
【4月更文挑战第13天】
15 0
什么是Java函数式接口?
|
15天前
|
设计模式 Java
Java接口与抽象类
Java接口与抽象类
17 0
|
19天前
|
安全 Java 编译器
接口之美,内部之妙:深入解析Java的接口与内部类
接口之美,内部之妙:深入解析Java的接口与内部类
37 0
接口之美,内部之妙:深入解析Java的接口与内部类
|
19天前
|
Java
Java中的抽象类和接口之间的异同点
抽象类顾名思义是之抽象出来的类,具有多个类的共性,是抽象的。抽象类和具体类是相对的概念。“抽象”是一种存在思想逻辑中的概念,而“具体”是一种可见可触摸的现实对象。简单说,法拉利,保时捷,兰博基尼,布加迪等多种车子,可以抽象出车,也可以抽象出跑车,这就是抽象出来的类别,称为抽象类。
10 0