JAVA平台的接口类比于C中的库.它对外提供了实现预期功能的API,供外部程序引用.我们只要事先"约定"好外部API及功能,上层开发和库开发可以做到并发进行.这种"约定"就类似于JAVA平台的宏观抽象.
1.JAVA平台的接口特性:
1).JAVA平台的接口中只能包含有常量、方法签名、和嵌套类型;
2).接口分两部分:声明和实现体,在接口声明中是没有方法体的实现,方法体的实现在类里面;
3).JAVA平台的接口没有构造器,所以它不能被实例化,它只能被类所实现或被另外的接口所继承.
4).JAVA中一个类只能继承自一个父类,但是接口可以是多重继承的.
2.定义接口的语法:
定义接口的语法的形式如下:
[修饰符] interface 接口名称 [extends 父接口名称列表]{
//接口体
}
1).修饰符:可选,用于指定接口的访问权限,要么是public要么是什么都没有,而不能使用protected或者private.如果什么都没有,则默认的访问权限是当前的软件包使用;
2).interface:定义接口的关键字;
3).接口名称:接口名称的首字母大写;
4).extends:父接口名称列表:可选,用于指定该接口继承自哪个父接口.如果是多继承,父接口之间必须用逗号分隔.如下:
public interface GroupedInterface extends Interface1,Interface2,Interface3{
//接口体
}
3.接口体的组成:
接口体由两部分组成:常量声明 + 方法声明.
3-1.接口体的语法:
[修饰符] interface 接口名称 [extends 父接口名称列表]{
//常量声明
[public] [static] [final] 常量名称;
//方法声明
[public] [abstract] 返回类型 方法签名;
}
1).常量声明:接口中所有字段都隐含具有public、static和final属性;
2).方法声明:接口中的方法只有返回类型和方法签名,是没有方法体.隐含属性具有public、abstract.
如下:
public interface GroupedInterface extends Interface1,Interface2,Interface3{
//常量声明
double E = 2.718282;
//上面的变量声明等价于public static final double E = 2.718282
//方法声明
void doSomething(int i,double x);
int doSomethingElse(String s);
}
4.实现接口
我们从上述知道,接口最核心部分还是方法体的实现.而在接口中只有方法的声明,并没有方法的具体实现.接口中的方法的具体实现需要在类里面实现.我们可以称实现了接口方法体的类为"接口类".
4-1.接口类的语法:
声明一个接口类,需要在类的声明中使用implements关键字.一个接口类可以实现多个接口,所以implements关键字后面要跟一个被类接口列表,用逗号分隔符分隔.语法形式如下:
[修饰符] class 类名称 [extends 父类名称] implement 接口列表{
//类体
//接口声明的方法的具体实现
}
4-2.示例:
下面考虑一个比较不同对象大小的接口实例Relatable.在接口Relatable中,声明一个isLargerThan方法,其作用是和另外一个实现了Relatable接口类型的对象进行比较.
1).Relatable接口声明如下:
public interface Relatable{
public int isLargerThan(Relatable other);
}
2).实现接口类:
public class RectanglePlus implements Relatable {
public int width = 0; //声明接口中的属性
public int height = 0;
public Point origin; //声明对象属性
// 四个构造器
public RectanglePlus() {
origin = new Point(0, 0);
}
public RectanglePlus(Point p) {
origin = p;
}
public RectanglePlus(int w, int h) {
origin = new Point(0, 0);
width = w;
height = h;
}
public RectanglePlus(Point p, int w, int h) {
origin = p;
width = w;
height = h;
}
// 移动矩形的方法
public void move(int x, int y) {
origin.x = x;
origin.y = y;
}
// 计算矩形面积的方法
public int getArea() {
return width * height;
}
// 实现接口Relatable中声明的方法
public int isLargerThan(Relatable other) {
RectanglePlus otherRect = (RectanglePlus)other; //对象类型转换
if (this.getArea() < otherRect.getArea()) //如果当前对象的面积小于参数对象的面积
return -1; //返回-1
else if (this.getArea() > otherRect.getArea()) //如果当前对象的面积大于参数对象的面积
return 1; //返回1
else
return 0; //如果当前对象与参数对象的面积相等,返回0
}
}
3).测试程序
public class ImlDemo1{
public static void main(String[] args){
RectanglePlus rect1 = new RectanglePlus(3,5); //创建实现了Relatable接口的RectanglePlus对象
RectanglePlus rect2 = new RectanglePlus(2,7); //创建实现了Relatable接口的RectanglePlus对象
int result = rect1.isLargerThan(rect2); //调用接口中的方法进行对象的比较
switch(result){ //根据比较的结果,输出相应的信息
case -1:
System.out.println("矩形rect1比矩形rect2小。");
break;
case 1:
System.out.println("矩形rect1比矩形rect2大。");
break;
default:
System.out.println("矩形rect1与矩形rect2一样大小。");
}
}
}
4).编译并运行带有接口的实例
上述有4个类型:Point类、RectanglePlus类、主类ImlDemo1和接口Relatable.要想让这个实例运行起来,可以有两种形式:
A.将4个类型分别保存为独立源文件(文件名必须和public类名一样).将4个源文件放在同一目录下.然后通过命令编译ImlDemo1.java;
B.将4个类型放在同一个源文件中,只保留一个public类(文件名必须和pblic类名一样).然后编译.
5.多个接口
JAVA平台,由于多个接口是多重继承的.如果一个类实现的两个接口中,两个接口都声明了相同的名称的常量或相同名称的方法,那么在实现这两个接口的类中,引用常量或实现方法时,就不明确是哪个接口中的.这是接口的冲突问题.如果是常量冲突,则需要在类中使用全限定名(接口名称.常量名称)明确指定常量所属的接口.如果是方法冲突,则只需要实现一个方法就可以了.
示例:
//位于文件Calculate.java中
public interface Calculate {
float PI = 3.14f; //定义一个用于表示圆周率的常量PI
float getArea( ); //定义一个用于计算面积的方法
float getGirth( ); //定义一个用于计算周长的方法
}
//位于文件Shape.java中
public interface Shape{
float PI = 3.14159f; //定义一个用于表示圆周率的常量PI
float getArea( ); //定义一个用于计算面积的方法
void draw( ); //定义一个绘制图形的方法
}
//位于文件Circle.java中
public class Circle implements Calculate,Shape {
//声明一个私有变量radius,代表圆的半径
private float radius;
//构造器,用于初始化半径radius
public Circle(float r){
radius = r;
}
//实现计算圆面积的方法,该方法两个接口中都有,只需实现一个既可
public float getArea( ){
float area = Calculate.PI*radius*radius; //计算圆的面积,使用全限定名指定Calculate接口中的常量
return area;
}
//实现计算圆周长的方法
public float getGirth ( ){
float circuGirth = 2*Shape.PI*radius ; //计算圆的面积,使用全限定名指定Shape接口中的常量
return circuGirth;
}
//实现绘制图形的方法
public void draw( ){
System.out.println("在这里绘制了一个圆!");
}
}
//位于文件ImlDemo2.java中
public class ImlDemo2 {
public static void main(String[ ] args){
Circle circle = new Circle(7);
float area = circle.getArea( );
System.out.println("圆的面积为:" + area);
float girth = circle.getGirth();
System.out.println("圆的周长为:" + girth);
circle.draw( );
}
}
简析:
在Circle.java中,它要引用的是类Shape中的常量PI.通过语句"Shape.PI"来指定.
6.将接口作为类型使用
接口可以看作和int等一样,是一种数据类型.比如定义一个接口类型的引用变量,那么,赋给这个变量的任何对象必须是实现这个接口的类的一个实例.
示例:
public Object findLargest(Object object1,Object object2){
Relatable obj1 = (Relatable)object1;
Relatable obj2 = (Relatable)object2;
if((obj1).isLargerThan(obj2) > 0)
return object1;
else
return object2;
}
简析:
上述示例中,object1、object2必须是实现了Relatable接口的类的实例对象.
因此,可以拋开对象实例的实现,如何继承,只要它实现了指定的接口,就可以进行"部分特征"的对比操作.
7.接口的改写
一个接口被声明后,有可能因为某种原因需要修改接口中的声明的方法(如增加新的方法、改变方法体的实现等).如下面这种情景:
程序猿A编写了一个名为DoIt的接口:
public interface DoIt{
void doSomething(int i,double x);
int doSomethingElse(String s);
}
因为某种原因,程序猿A要给这个接口增加第三个方法.如下:
public interface DoIt{
void doSomething(int i,double x);
void doSomethingElse(String s);
boolean didItWork(int i,double x,String s);
}
这时候,旧接口没有办法使用新增接口.这时候最合适的方法就是,保持原来的接口不动,从原来的旧接口派生一个新的接口,在新的接口中增加第三个方法.如下:
public interface DoItPlus extends DoIt{
boolean didItWork(int i,double x,String s);
}
其他人可以选择用旧的接口,也可以升级到新的接口,而避免了接口程序不能使用的情况.