1、前言
首先明确一点,本文接下来描述的接口,遵守的是Java的默认行为,比如在定义接口的方法时,没有加任何特定的关键字,这个时候java会默认为方法加上public访问控制符,在为接口定义成员时,Java会自动为其加上public static final修饰。在默认行为下,接口看起来会更纯粹。
在JAVA最新的版本中,接口已经不像刚开始时那样单纯了。现在的版本中,可以通过default关键字为接口提供实现,可以在接口中定义private方法,将default实现中的共通逻辑放在private方法中,同时对外隐藏实现细节,也可以定义私有的static类型的成员供private方法使用,越来越复杂了。
其实这些特性并非完全必要,新特性提供了一些便利,但是却使接口的定义背离了初衷并且变得更加复杂。并且提供的这些新特性本来就有替代方案,为接口实现的这些新特性其实是抽象类的功能。因此,把接口单纯的看成是定义public static final成员与public方法的地方可能更好一点,所以本文忽略接口的这些新特性。
2、接口
Java中接口的含义比抽象类更进一步,抽象类中可以混合包含抽象方法与普通方法,但是接口中的方法全部都是抽象的,不用abstract关键字特别指定,全部不允许有具体的实现(注意前掉),当然接口本身也是抽象的,不允许实例化(同样注意前掉)。另外接口也可以包含成员,但有两个限制,需要记住这一点。
成员用static final修饰,实际用户不需要特别设置,编译器会自动添加。
另一个是成员只能在定义时通过赋值初始化,而不能通过initializer初始化,如:
interface Instrument {
// 合法的成员安始化
String myName = "Instrument";
void play(String song);
void sing (String song);
}
interface Instrument {
// 非法
String myName;
static {
myName = "Instrument";
}
void play(String song);
void sing (String song);
}
只所有会这样,是因为接口是纯抽象,不允许里边有具体的方法,而{ x = 1;y=2}这种写法等同与Java中的initializer方法,因此是不允许的。
接下来展示一个接口与实现的具体示例:
interface Instrument {
String myName = "Instrument";
void play(String song);
void sing(String song);
}
interface People {
String myName = "People";
void play(String song);
void say(String song);
}
class Dog {
public void say(String song) {
System.out.println("Dog.say():" + song);
}
}
class Wind extends Dog implements Instrument, People {
@Override
public void play(String song) {
System.out.println("Wind.play():" + song);
}
@Override
public void sing(String song) {
System.out.println("Wind.sing():" + song);
}
}
示例展示了很多东西,class Wind extends Dog implements Instrument, People这句话,表示的意思是Wind类是从Dog类继承而来的,并且实现了Instrument与People两个接口。
在Java中,子类的父类只能有一个,不能像Java一样有多个,并且如果有父类并且同时也实现了其它接口,那么extend关键字一定要出现在implements关键字前边,否则报错。implements关键字后边接实现的接口,可以有多个,这个叫做多重实现。
如果类实现了某个接口,那么类一定要实现接口中定义的所有方法,否则类将会是abstract的,需要在类名前边加上abstract关键字。
一个需要注意的事情,Instrument与People都定义了方法void play(String song),这两个方法的特征值完全相同,那么在Wind中实现的play()方法,到底是来自于Instrument呢还是People呢?答案是如果实现的多个接口中有方法名冲突的话,那就合并成一个。换句话说,Wind中的play()方法既是Instrument中play()方法的实现,也是People中play()方法的实现。很显然的问题,单从接口名字上来看,Instrument中的play()方法与People中的play()方法是不同的,应该有各自的实现,而不是共用相同的实现,因为这两个接口的意图是完全不同的,怎么能用相同的实现呢?但在目前的方式下,还没有办法在Wind中分别为Instrument与People接口提供不的实现。
另一个事情,People接口中有say()方法,但在Wind中却没有为其实现。按前面的说法,如果有接口的方法没有实现,那么当前类应该是抽象类,前边要加abstract,但Wind类没有加,也没有报错。这是因为Wind是从Dog继承而来,Wind虽然没有提供,但是Dog提供了,相当于是间接提供实现。总之Java会在当前类的继承路径上检索,只要在路径上找到找到匹配的方法,那么它就认为接口已经实现了。如果Dog中的say()方法是私有的话,则除Dog类以外的所有类,这个方法都不可见,因此这个事情仍然要在Wind中为People中的say()提供实现。
此时Wind实例,既可以向上转型成Instrument、People接口,也可以向上转型成Dog类,如下都中合法的:
public static void main(String[] args) {
Instrument ins = new Wind();
People peo = new Wind();
Dog dog = new Wind();
}
接口可以通过extends进行扩展,如下:
interface CommonDog {
void WagTail();
}
interface SportDog {
void Run();
}
interface MadDog extends CommonDog, SportDog {
void Bite();
}
接口的扩展与类的扩展有一点不一样,类的扩展中,extends关键字后边只能有一个父类,但扩展接口时,extends后边可以有多个接口。这样子就得到一个新接口,它同时有WagTail、Run()、Bite()三个接口。