Instrument 类可以很容易转换成abstract类。既然使某个类成为抽象类并不需要所有方法都是抽象的,所以仅需要将某些方法声明为抽象即可。
下面是修改过的“管弦乐器”的例子,其中采用了抽象类和抽象方法:
enum Node{
MIDDLE_C ,
C_SHARP ,
B_FLAT ;
}
//abstract关键字允许人们在类中创建一个或多个没有任何定义的方法
abstract class Instrument{
private int i;
public abstract void play(Node n);
public String what(){
return "Instrument";
}
public abstract void adjust();
}
class Wind extends Instrument{
public void play(Node n){
System.out.println("Wind.play()" + n);
}
public String what(){
return "Wind";
}
public void adjust(){}
}
class Percussion extends Instrument{
public void play(Node n){
System.out.println("Percussion.play()" + n);
}
public String what(){
return "Percussion";
}
public void adjust(){}
}
class Stringed extends Instrument{
public void play(Node n){
System.out.println("Stringed.play()" + n);
}
public String what(){
return "Stringed";
}
public void adjust(){}
}
class Brass extends Wind{
public void play(Node n){
System.out.println("Brass.play()" + n);
}
public void adjust(){
System.out.println("Brass.adjust()");
}
}
class Woodwind extends Wind{
public void play(Node n){
System.out.println("Woodwind.play()" + n);
}
public String what(){
return "Woodwind";
}
}
public class Music4 {
//多态
static void tune(Instrument i){
i.play(Node.MIDDLE_C);
}
static void tuneAll(Instrument[] e){
for(Instrument i : e){
tune(i);
}
}
public static void main(String[] args){
Instrument[] orchestra = {
new Wind(),new Percussion(),new Stringed(),new Brass(),new Woodwind()
};
tuneAll(orchestra);
}
}
创建抽象类和抽象方法非常有用,因为他们可以使类的抽象性明确起来,并告诉用户和编译器打算怎么来使用他们。
接口
interface关键字使抽象的概念更向前迈进了一步。abstract关键字允许人们在类中创建一个或多个没有任何定义的方法——提供了接口部分,但是没有提供任何相应的具体实现,这些实习是由此类的继承者创建的。interface这个关键字产生一个完全抽象的类,它根本没有提供任何具体实现。它允许创建者确定方法名、参数列表和返回类型,但是没有任何方法体。接口只提供了形式,从未提供任何具体实现。
一个接口表示:“所以实现了该特定接口的类看起来都像这样”。因此,任何使用某特定接口的代码都知道可以调用该接口的哪些方法,而且仅需要知道这些。因此,接口被用来建立类与类之间的协议。
要想创建一个接口,需要用interface关键字来替代class关键字,就像类一样,可以在interface关键字前面添加public关键字。如果不添加public关键字,则它只具有包访问权限。接口也可以包含域,但这些域隐式地是static和final的。
要让一个类遵循某个特定的接口,需要使用implement关键字,它表示:“interface只是它的外貌,但是现在我要声明它是如何工作的”。除此之外,它看起来还很像继承
可以从Woodwind和Brass看出来,一旦实现了某个接口,其实现就变成了一个普通的类,可以按常规方式扩展它
enum Node{
MIDDLE_C ,
C_SHARP ,
B_FLAT ;
}
interface Instrument{
int value = 5; //static 和 final
//无方法定义
void play(Node n);//自动是public访问权限
void adjust();
}
class Wind implements Instrument{
public void play(Node n){
System.out.println(this + ".play()" + n);
}
public String toString(){
return "Wind";
}
public void adjust(){
System.out.println(this + ".adjust()");
}
}
class Percussion implements Instrument{
public void play(Node n){
System.out.println(this + ".play()" + n);
}
public String toString(){
return "Percussion";
}
public void adjust(){
System.out.println(this + ".adjust()");
}
}
class Stringed implements Instrument{
public void play(Node n){
System.out.println(this + ".play()" + n);
}
public String toString(){
return "Stringed";
}
public void adjust(){
System.out.println(this + ".adjust()");
}
}
class Brass extends Wind{
public String toString(){
return "Brass";
}
}
class Woodwind extends Wind{
public String toString(){
return "Woodwind";
}
}
public class Music4{
static void tune(Instrument i){
i.play(Node.MIDDLE_C);
}
static void tuneAll(Instrument[] e){
for(Instrument i : e){
tune(i);
}
}
public static void main(String[] args){
Instrument[] orchestra = {
new Wind(),new Percussion(),new Stringed(),new Brass(),new Woodwind()
};
tuneAll(orchestra);
}
}
what()方法已经被修改为toString()方法,因为toString()逻辑正是what()要实现的逻辑,由于toString()方法是跟类Object的一部分,因此它不需要出现在接口中。
无论是将其向上转型为称为Instrument的普通类还是抽象类,亦或是称作Instrument的接口,都不会有问题。它的行为都是相同的。
9.5通过继承来扩展接口
通过继承,可以很容易地在接口中添加新的方法声明,还可以通过继承在新接口中组合数个接口。这两种情况都可以获得新的接口,如下:
interface Monster{
void menace();
}
interface DangerousMonster extends Monster{
void destroy();
}
interface Lethal{
void kill();
}
class DragonZilla implements DangerousMonster{
public void menace(){}
public void destroy(){}
}
interface Vampire extends DangerousMonster , Lethal{
void drinkBlood();
}
class veryBadVampire implements Vampire{
public void menace(){}
public void destroy(){}
public void kill(){}
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);
}
}
DangerousMonster是Monster的直接扩展,它产生了一个新接口。DragonZilla中实现了这个接口。
在Vampire中使用的语法仅适用于接口继承。一般情况下,只可以将extends用于单一类,但是可以用于多个基类接口。就像所看到的,只需用逗号将接口名一一分隔开。
初始化接口中的域
在接口中定义的域不能是“空final”,但是可以被非常量表达式初始化。
interface RandVal{
Random RAND = new Random(47);
int Rand_int = RAND.nextInt();
long Rand_long = RAND.nextLong();
float Rand_float = RAND.nextFloat();
double Rand_double = RAND.nextDouble();
}
既然域是static的,它们就可以在类第一次被加载时初始化,这发生在任何域首次被访问时。
import java.util.Random;
interface RandVal{
Random RAND = new Random(47);
int Rand_int = RAND.nextInt();
long Rand_long = RAND.nextLong();
float Rand_float = RAND.nextFloat();
double Rand_double = RAND.nextDouble();
}
public class TestRandVal {
public static void main(String[] args){
System.out.println(RandVal.Rand_int);
System.out.println(RandVal.Rand_long);
System.out.println(RandVal.Rand_float);
System.out.println(RandVal.Rand_double);
}
}
当然这些域不是接口的一部分,它们的值被存储在该接口的静态存储区域。