在JAVA中,“interface”关键字用来表示接口,正是interface的使用,使得抽象的概念更加深入人心。
“abstract”关键字允许我们在类中创建一个或多个没有定义的方法,提供了接口部分,但没有提供任何的具体实现,这些实现由此类的继承者们实现。
而interface则会产生一个完全抽象的类,根本没有任何的具体实现,它允许创建者确定方法名、参数列表和返回类型,但没有任何方法体,接口只是提供了形式,而未提供任何具体实现。但是interface不仅仅是一个极度抽象的类,因为其允许我们创建一个能够被向上转型为多种基类的类型,来实现某种类似多重继变种的特性。
要想创建一个接口,需要用interface来代替class,就像类一样,可以在其前面加public关键字,但也仅限于该接口与在其同名的文件中被定义的时候,若不添加public,则默认为包访问权限,只能在同一个包中使用,接口可以保护域,但这些域隐式的是static和fianl的。要让一个类遵循某个特定的接口,需要使用“implement”关键字,看起来很像继承。
可以在接口中显示的将方法声明为public,但即使不这样做,依旧是public的,因此当实现某个接口时,在接口中被定义的方法必须被定义为public,否则在方法继承的过程中,其可访问权限会降低,JAVA不允许这样做。看下面一段代码:
enum Note{
MIDDLE_C,C_SHARP,BFLAT;
}
interface Instrument{
int VALUE = 5;
void play(Note n);
void adjust();
}
class Wind implements Instrument {
public void play(Note n){
System.out.println(this + ".play() " + n);
}
public String toString(){
return "Wind";
}
public void adjust(){
System.out.println(this + ".adjust()");
}
}
class Percussion implements Instrument{
@Override
public void play(Note n) {
// TODO Auto-generated method stub
System.out.println(this + ".play()" + n);
}
public String toString(){
return "Percussion";
}
@Override
public void adjust() {
// TODO Auto-generated method stub
System.out.println(this + ".adjust()");
}
}
class Stringed implements Instrument{
@Override
public void play(Note n) {
// TODO Auto-generated method stub
System.out.println(this + ".play()" + n);
}
public String toString(){
return "Stringed";
}
@Override
public void adjust() {
// TODO Auto-generated method stub
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 Music {
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) {
// TODO Auto-generated method stub
Instrument[] orchestra = {
new Wind(),
new Percussion(),
new Stringed(),
new Brass(),
new Woodwind()
};
tuneAll(orchestra);
}
}
此程序运行结果为:
无论将其上转为Instrument的普通类、抽象类还是接口,都不会有任何问题,它的行为是相同的。
在JAVA中,基类的继承只允许有一个,也就是说无法像C++那样支持多继承,但是却可以通过接口的方式来间接的实现多继承。JAVA支持多个接口的继承并可以向上转型为每个接口,因为每个接口都是一个独立类型,看下面一段代码:
package access;
interface CanFight{
void fight();
}
interface CanSwim{
void swim();
}
interface CanFly{
void fly();
}
class ActionCharacter{
public void fight(){}
}
class Hero extends ActionCharacter
implements CanFight,CanSwim,CanFly{
public void swim() {}
public void fly(){}
}
public class Adventure {
public static void t(CanFight x){
x.fight();
}
public static void u(CanSwim x){
x.swim();
}
public static void v(CanFly x){
x.fly();
}
public static void w(ActionCharacter x){
x.fight();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Hero h = new Hero();
t(h);
u(h);
v(h);
w(h);
}
}
可以看到Hero结合了具体类ActionCharacter和接口CanFight、CanSwim和CanFly。可以扩展接口,但得到的只是另一个接口,当想要创建对象时,所有的定义首先必须全部存在。
使用接口的核心原因有:
1.为了能够向上转型为多个基类型,这样做带来极大的灵活性。
2.防止客户程序员创建该类的对象,并确保这仅仅是一个接口。
如果要创建不带任何方法定义和成员变量的基类,那么就该选择接口而不是抽象类,事实上,如果知道某事物应该成为一个基类,那么就该第一选择为接口。
下面给出一个通过继承来扩展接口的例子:
package access;
interface Monster{
void menace();
}
interface DangerousMonster extends Monster{
void destroy();
}
interface Lethal{
void kill();
}
class DragonZilla implements DangerousMonster{
@Override
public void menace() {
// TODO Auto-generated method stub
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
}
interface Vampire extends DangerousMonster,Lethal{
void drinkBlood();
}
class VeryBadVampire implements Vampire{
@Override
public void destroy() {
// TODO Auto-generated method stub
}
@Override
public void menace() {
// TODO Auto-generated method stub
}
@Override
public void kill() {
// TODO Auto-generated method stub
}
@Override
public void drinkBlood() {
// TODO Auto-generated method stub
}
}
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) {
// TODO Auto-generated method stub
DangerousMonster barney = new DragonZilla();
u(barney);
v(barney);
Vampire vlad = new VeryBadVampire();
u(vlad);
v(vlad);
w(vlad);
}
}
在实现多重继承的时候,可能会遇到一些小麻烦,在某个类或接口中若含有相同的方法名称但返回类型不同时,会造成很大的混乱,在打算组合的不同接口中使用相同的方法名通常会造成代码可读性的混乱,尽量避免此情况的发生。