重温经典《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月前
|
JSON Java Apache
非常实用的Http应用框架,杜绝Java Http 接口对接繁琐编程
UniHttp 是一个声明式的 HTTP 接口对接框架,帮助开发者快速对接第三方 HTTP 接口。通过 @HttpApi 注解定义接口,使用 @GetHttpInterface 和 @PostHttpInterface 等注解配置请求方法和参数。支持自定义代理逻辑、全局请求参数、错误处理和连接池配置,提高代码的内聚性和可读性。
129 3
|
5天前
|
安全 Java API
java如何请求接口然后终止某个线程
通过本文的介绍,您应该能够理解如何在Java中请求接口并根据返回结果终止某个线程。合理使用标志位或 `interrupt`方法可以确保线程的安全终止,而处理好网络请求中的各种异常情况,可以提高程序的稳定性和可靠性。
35 6
|
22天前
|
Java API
Java中内置的函数式接口
Java中内置的函数式接口
23 2
|
27天前
|
Java
在Java中如何实现接口?
实现接口是 Java 编程中的一个重要环节,它有助于提高代码的规范性、可扩展性和复用性。通过正确地实现接口,可以使代码更加灵活、易于维护和扩展。
47 3
|
26天前
|
Java
在Java中,接口之间可以继承吗?
接口继承是一种重要的机制,它允许一个接口从另一个或多个接口继承方法和常量。
74 1
|
26天前
|
Java 开发者
在 Java 中,一个类可以实现多个接口吗?
这是 Java 面向对象编程的一个重要特性,它提供了极大的灵活性和扩展性。
57 1
|
26天前
|
Java
在Java中实现接口的具体代码示例
可以根据具体的需求,创建更多的类来实现这个接口,以满足不同形状的计算需求。希望这个示例对你理解在 Java 中如何实现接口有所帮助。
41 1
|
1月前
|
Java Android开发
Eclipse 创建 Java 接口
Eclipse 创建 Java 接口
27 1
|
1月前
|
Java
java线程接口
Thread的构造方法创建对象的时候传入了Runnable接口的对象 ,Runnable接口对象重写run方法相当于指定线程任务,创建线程的时候绑定了该线程对象要干的任务。 Runnable的对象称之为:线程任务对象 不是线程对象 必须要交给Thread线程对象。 通过Thread的构造方法, 就可以把任务对象Runnable,绑定到Thread对象中, 将来执行start方法,就会自动执行Runable实现类对象中的run里面的内容。
39 1
|
1月前
|
Java 开发者
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
46 4