首页 > 代码库 > JAVA编程思想(5) - 接口(一)

JAVA编程思想(5) - 接口(一)

接口

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

抽象类和抽象方法

  • 在之前“乐器”的所有例子中,基类Instrument中的方法往往是“哑”的方法,若要调用这些方法的话,就会出现一些错误。。这是因为Instrument类的目的是为它的所有的导出类创建一个通用接口
  • 在那些例子中,建立这个通用接口的唯一理由是,不同的子类可以用不同的方式表示此接口。通用接口建立起一种基本形式,以此表示所有导出类的共同部分。另一种说法是将Instrument类称作抽象类基类,或简称抽象类
  • 创建一个抽象类的对象是没有任何意义的,并且我们可能还想阻止使用者这么做,通过让Instrument中所有的方法都产生错误,就可以实现这个目的。我们希望能在编译时就能够捕捉到这种错误。
  • 为此,Java提供一种叫做抽象方法的机制,这种方法是不完整的;仅有声明没有方法体。例如,抽象方法的声明采用的语法是:*abstract void f();
  • 包含抽象方法的类叫做抽象类。如果一个类包含一个或者多个抽象方法,该类就被限定为抽象的。(否则编译器会报错。)
  • 由于为抽象类创建对象是不安全的,因此编译器会报错,以此确保抽象类的纯粹性,我们不必担心会误用它。
  • 我们也有可能会创建一个没有任何抽象方法的抽象类。如果,我们希望让一个类的方法显得没有实际意义,而且我们也想要阻止这个类产生任何对象的话,那么这么做就很有用。
//: interfaces/music4/Music4.java
// Abstract classes and methods.
package interfaces.music4;
import polymorphism.music.Note;
import static net.mindview.util.Print.*;

abstract class Instrument {
  private int i; // Storage allocated for each
  public abstract void play(Note n);
  public String what() { return "Instrument"; }
  public abstract void adjust();
}

class Wind extends Instrument {
  public void play(Note n) {
    print("Wind.play() " + n);
  }
  public String what() { return "Wind"; }
  public void adjust() {}
}

class Percussion extends Instrument {
  public void play(Note n) {
    print("Percussion.play() " + n);
  }
  public String what() { return "Percussion"; }
  public void adjust() {}
}

class Stringed extends Instrument {
  public void play(Note n) {
    print("Stringed.play() " + n);
  }
  public String what() { return "Stringed"; }
  public void adjust() {}
}    

class Brass extends Wind {
  public void play(Note n) {
    print("Brass.play() " + n);
  }
  public void adjust() { print("Brass.adjust()"); }
}

class Woodwind extends Wind {
  public void play(Note n) {
    print("Woodwind.play() " + n);
  }
  public String what() { return "Woodwind"; }
}    

public class Music4 {
  // Doesn‘t care about type, so new types
  // added to the system still work right:
  static void tune(Instrument i) {
    // ...
    i.play(Note.MIDDLE_C);
  }
  static void tuneAll(Instrument[] e) {
    for(Instrument i : e)
      tune(i);
  }    
  public static void main(String[] args) {
    // Upcasting during addition to the array:
    Instrument[] orchestra = {
      new Wind(),
      new Percussion(),
      new Stringed(),
      new Brass(),
      new Woodwind()
    };
    tuneAll(orchestra);
  }
} /* Output:
Wind.play() MIDDLE_C
Percussion.play() MIDDLE_C
Stringed.play() MIDDLE_C
Brass.play() MIDDLE_C
Woodwind.play() MIDDLE_C
*///:~
  • 可以看到除了基类,实际上并没有什么改变。
  • 创建抽象类和抽象方法是非常有用的。因为它们可以使类的抽象性明确起来,并告诉用户和编译器打算怎么使用它们。抽象类还是很有用的重构工具,因为它们使得我们可以很容易的将公共方法沿着继承层次结构往上移动。

接口

  • interface关键字使抽象的概念更进一步。abstract关键字允许人们在类中创建一个或者多个没有任何定义的方法——提供了接口部分。但是没有提供任何具体的实现。这些实现是有此类的继承者创建的。interface这个关键字产生了一个完全抽象的类,它根本就没有任何实现,它允许创建者确定方法名,参数列表和返回类型,但是没有任何方法体,接口只提供了形式,而未提供任何具体实现。
  • 一个接口表示:“所有实现了该特定接口的类看起来都像这样”。因此任何使用该接口的代码都知道可以调用该接口的哪些方法,而且仅知道这些。因此,接口被用来建立类与类之间的协议。(某些面向对象的编程语言使用关键字protocol来实现功能。)
  • interface不仅仅是一个极度抽象的的类,因为它允许人们通过建立一个能够被向上转型为多种基类的类型,来实现某种类似多重继变种的特性。(java仅支持单继承。)
  • 接口也包含域,但是这些域隐式地是staticfinal的。接口中的方法无论是你是显示的声明为public的,还是没有声明,它都默认是public的,否则,它们将只能得到默认的包访问权限,这样在继承的过程中,其访问权限就被降低了,这是java编译器不允许的。
//: interfaces/music5/Music5.java
// Interfaces.
package interfaces.music5;
import polymorphism.music.Note;
import static net.mindview.util.Print.*;

interface Instrument {
  // Compile-time constant:
  int VALUE = http://www.mamicode.com/5; // static & final
  // Cannot have method definitions:
  void play(Note n); // Automatically public
  void adjust();
}

class Wind implements Instrument {
  public void play(Note n) {
    print(this + ".play() " + n);
  }
  public String toString() { return "Wind"; }
  public void adjust() { print(this + ".adjust()"); }
}

class Percussion implements Instrument {
  public void play(Note n) {
    print(this + ".play() " + n);
  }
  public String toString() { return "Percussion"; }
  public void adjust() { print(this + ".adjust()"); }
}

class Stringed implements Instrument {
  public void play(Note n) {
    print(this + ".play() " + n);
  }
  public String toString() { return "Stringed"; }
  public void adjust() { print(this + ".adjust()"); }
}

class Brass extends Wind {
  public String toString() { return "Brass"; }
}    

class Woodwind extends Wind {
  public String toString() { return "Woodwind"; }
}

public class Music5 {
  // Doesn‘t care about type, so new types
  // added to the system still work right:
  static void tune(Instrument i) {
    // ...
    i.play(Note.MIDDLE_C);
  }
  static void tuneAll(Instrument[] e) {
    for(Instrument i : e)
      tune(i);
  }    
  public static void main(String[] args) {
    // Upcasting during addition to the array:
    Instrument[] orchestra = {
      new Wind(),
      new Percussion(),
      new Stringed(),
      new Brass(),
      new Woodwind()
    };
    tuneAll(orchestra);
  }
} /* Output:
Wind.play() MIDDLE_C
Percussion.play() MIDDLE_C
Stringed.play() MIDDLE_C
Brass.play() MIDDLE_C
Woodwind.play() MIDDLE_C
*///:~
  • 此实例还有一个改动的地方是:what()方法已经被修改为toString()方法,因为toString()的逻辑正是what()要实现的逻辑,由于toStirng()方法是根类Object的一部分,因此它不需要出现在接口中。
  • 余下的代码其工作方式都是相同的。无论你是将其向上转型为Instrument的普通类还是还是它的抽象类异或是它的接口,都不会出现问题,它的行为都是相同的,事实上你也无法知道它是一个普通类、抽象类还是一个接口。

JAVA编程思想(5) - 接口(一)