4.1 Java接口
Java接口(interface)是一些抽象方法和固定变量(常量)的集合,interface有时被翻译为“界面”,即指类与类中的变量和方法之间“交界处”的交互协议。
Java接口定义了一个实体可能发出的动作原型,但是没有实现,给定一个协议或一个规范,Java类则是实现协议,满足规范的具体实体。因此,Java接口最重要的功能是指定实现接口的类需要“做什么”,在接口定义中并不考虑“怎么做”,具体“怎么做”需要通过实现接口的类来完成。
在Java编程中允许几个类同时享有一个或多个程序设计接口,而彼此不完全知道对方具体的实现方法。正因为如此,Java接口为设计和规划大型类或应用程序提供了一种方便的程序设计框架,是程序设计者定义出的一个类所需要实现一些功能的集合。依据Java接口机制,设计者很容易地将大型应用程序进行细化,然后再通过接口的形式分配给每位编程人员,使得各个程序模块的功能更清晰,接口使编程人员可以指定实现给定接口的对象,而不必知道该对象的具体类型或继承关系等。
4.1.1 接口的定义
Java接口封装了一组方法和常量,在接口中定义的方法只提供了方法协议的封装,而没有实现方法的实体,它不限制实现其实体需要在什么类上的继承。声明一个Java接口的语法格式为:
接口作为一些方法和常量的集合,通过interface关键字将它作为一种类型声明,按照Java的语法规则,当接口作为类型时,其行为和类作为类型完全一样。
Java接口的修饰符选项有public、protected和abstract等,指明接口的作用域和性质,Java接口不能被修饰为private,接口定义的规则与定义类是一样的,只是使用的关键字interface与定义类class不同而已。
同Java类的定义一样,Java接口的成员也是数据和操作的组合,只是在Java体系中,Java接口被归为具有抽象性质的一种类型,对其内部的数据和操作有一些特殊的要求:接口中的数据被要求声明为常量,并赋予初始值,常量只能是public(公共的)、static(静态的)和final(最终的);接口中表现操作的方法被要求定义为“空”方法,即只有方法的声明,没有方法体的内容,接口内部方法声明的语法格式为:
在Java接口里定义的方法只是规定了方法的输入(parameterList)、输出(returnType)协议,没有方法体,即没有任何操作代码。
在Java接口内部的方法定义中,既然没有形式上的显式声明(例如无修饰符),其修饰符只能是public(公共的)或者abstract(抽象的)。
【示例4-1】 Java接口应用程序,该接口描述的是计算圆的面积和周长。其接口代码为:
4.1.2 接口的实现
Java接口是不能直接使用的,它需要由一个类实现(或称为继承)后方可使用,类实现接口的关键字是implements,但是实现了接口的类并没有继承任何东西,只是承诺实现接口中定义的成员,具体实现多少、怎样实现并没有任何限制。
一个类实现一个接口的语法格式为:
关键字implements用在类声明中,在implements后面指出了该类使用的接口,即要实现(继承)的接口,当一个类通过implements关键字实现一个接口时,一般需要实现接口中描述的所有方法的实体,除非实现接口的类是抽象类。
在实现接口的类中,与类的继承相同,接口作为“父类”,常量自然继承到类中,而接口中的方法需要在继承的类中被覆盖,要覆盖被实现接口中的所有方法,它是方法覆盖的另一种应用,因为在接口中定义的方法只能被修饰为public或者abstract,因此,在继承的类中,覆盖的方法也只能被修饰为public或者abstract。
被实现的接口与该类的关系是接口只负责定义框架(方法),不实现任何操作功能,其操作功能的实现是由实现了该接口的类完成的,在类的方法体中编写操作代码。
【示例4-2】 实现计算圆面积和周长接口(其代码见4.1.1节)的类代码以及应用该类的示例程序。
在Java体系中,与类继承的单一性相比较,一个类并非只能继承(实现)一个接口,一个类可以实现多个接口,实现多个接口时对每个接口而言与实现一个接口是等同的,实现关系如图4-1所示,实现的所有接口是一视同仁的。
图4-1 一个类实现多个接口
一个类实现多个接口的语法格式为:
当一个类需要实现多个接口时,在类声明中implements关键字后的接口名称之间需要使用逗号分开。
【示例4-3】 定义了两个接口的Java程序代码。一个接口描述在直角坐标系中的点Point,另一个接口描述在直角坐标系中线段的长度Length。
在直角坐标系中,一个圆是由圆心和半径描述的,其实现上述两个接口的描述圆的类Circle代码为:
4.1.3 接口的继承
在Java体系中,与类继承一样,Java接口也有继承关系,其继承并非是单一的。由于Java类不支持多重继承,但是,在现实世界中多重继承往往是需要的,为实现多重继承,Java则需要使用接口实现,接口的继承也是通过extends关键字实现的。
接口多重继承语法格式为:
Java接口的多重继承其实也是将多个接口进行重新组合,即利用extends关键字把多个接口组合为一个接口,被继承的多个接口是同等的,其继承关系如图4-2所示,子接口是完全继承了所有的父接口内容。
图4-2 接口的多重继承
【示例4-4】 将Point和Length接口(其代码见4.1.2节)作为父接口,定义一个继承它们的描述圆在直角坐标系中位置的子接口,并添加一个设置圆在直角坐标系中位置的方法,其子接口代码为:
当一个类实现一个子接口时,除了需要覆盖子接口中的方法外,还需要覆盖父接口中的所有方法。定义一个实现Circle接口、描述圆在直角坐标系中位置的类,其程序代码为:
4.1.4 Java类同时继承父类并实现接口
在Java体系中,当一个子类继承一个父类的同时,还可以实现多个接口,其语法格式为:
【示例4-5】 子类继承父类的同时还实现一个接口的程序示例。该程序首先定义一个描述人行走行为的类PeopleMove,因为所有正常的成年人行走的方式和速度都相差不大,可以认为是一样的;然后定义一个描述人说话行为的接口PeopleSpeak,因为人说话因区域不同差异很大,需要根据区域确定;最后定义一个继承PeopleMove类和实现PeopleSpeak接口的描述美国人的类American,在American类中覆盖接口定义的说话行为的方法,使其符合美国人的说话行为。其程序代码为:
4.1.5 接口与Java抽象类
在Java体系中,Java接口实际上就是抽象接口,因为在Java接口中只是声明其成员,并不实现具体的操作,因此,在接口的声明中没有任何修饰符的情况下,该接口被自动修饰为abstract和public(默认的)。
Java接口与抽象类比较相似,不同之处在于接口中的成员变量需要赋予初始值,抽象类中的变量可以不用赋予初始值;再有抽象类中可以定义一般方法和抽象方法,而接口中只能定义抽象方法,在Java接口中声明的变量被自动修饰为final型(默认的),而定义的方法被自动修饰为abstract(默认的),因此,可以将Java接口看成是“纯”的抽象类。
同Java普通类一样,一个抽象类可以实现一个或多个接口,在抽象类中可以覆盖接口中声明的方法,也可以不覆盖接口中声明的方法,将覆盖接口中方法的任务交给抽象类的子类完成。在抽象类中可以定义抽象和普通方法,当覆盖接口中声明的方法而不实现具体的操作(不实现方法内容)时,可以将该方法再修饰为abstract,而在抽象类中用普通方法覆盖接口中声明的方法时,则需要为该方法编写具体的内容(操作)。
【示例4-6】 定义一个描述动物各种行为的接口Animal和实现该接口的描述猫科动物的类Catamount。猫科动物有猫和虎等,它们的有些行为是不一样的,因此,猫科动物类将被定义为抽象类。在抽象类中可以覆盖接口中的方法。也可以不覆盖接口中的方法。该程序代码为:
在继承接口的抽象类中可以不用实现接口中的方法,抽象类可以把实现接口方法的任务交给抽象类的子类,另外,继承抽象类的子类除了需要实现抽象类中的抽象方法外,还需要实现抽象类继承的接口中的没有被覆盖的方法。例如,下面的程序是定义一个继承Catamount类的描述猫的子类Cat,在Cat类中除了需要实现Move()和Shout()方法外,还需要实现接口中的Drink()方法,其程序代码为:
4.1.6 接口的应用
在用Java语言编写程序时,使用Java接口机制可以增加编程的灵活性。例如,使用Java接口定义一个类的表现形式,不做任何具体的操作,当接口被实现时,可以通过不同的Java类实现不同的操作,因此,使用Java接口机制可充分体现面向对象编程的继承性和多态性等。
【示例4-7】 描述动物的Java程序。“动物”是一个抽象的概念,将动物的各种行为能力通过接口抽象地独立描述,然后再通过继承的组合可以分别描述各大种类的动物,例如鸟类、鱼类动物等。当确定具体动物时,根据具体动物选择实现的接口,并重写其行为方法。
动物可能具有的能力有:叫喊能力、飞行能力、行走能力、游泳能力等,为每种能力定义一个独立的接口,其代码为:
当描述鸟类动物时,其具备上述各种能力,因此,鸟类的接口将继承上述所有接口,鸟类的接口代码为:
当具体到海鸥动物时,定义的海鸥类实现鸟类BirdAnimal接口,并覆盖所有接口中的方法,重写海鸥所具有的行为方式,其定义的类代码和应用程序代码为:
当描述鱼类动物时,其只有游泳能力,因此,鱼类的接口只继承Swim接口就可以了。鱼类的接口代码为:
当具体到金鱼动物时,定义的金鱼类实现鱼FishAnimal接口,并覆盖接口中的方法,重写金鱼所具有的行为方式,其定义的类代码和应用程序代码为:
有些水生动物是可以登上陆地的,例如乌龟,当定义乌龟类时,假设乌龟与金鱼的差别只有乌龟可以在陆地上行走一项,则可以通过继承金鱼类并实现行走能力接口定义乌龟类,其类定义和使用代码为:
当然,还可以根据类的继承和接口多重继承的不同组合定义两栖动物,陆路动物等接口和定义具体动物的类,还可以在确定了具体动物的同时添加实现某些行为能力的接口等,并在覆盖的方法中为具体动物填写所具有的行为能力。
接口的多重继承、一个类在继承父类的同时可以实现多个接口的特性,以及通过接口规定一个类要做什么,而不参与如何去做等特性体现了Java语言编程的灵活性,而在实现接口的类中方法的覆盖和重写特性体现了Java语言编程的多态性。
【示例4-8】 显示接口变量在Java程序运行时的动态绑定特性,体现一个接口,多个方法的动态的多态特性。
UseClassAandB程序输出显示结果为:
另外,在Java接口中,定义的变量都具有static和final属性,因此,可以利用接口定义整个应用程序使用的全局变量,在整个应用程序中可以直接使用。
【示例4-9】 定义常量的接口程序和直接引用接口中常量的Java程序。
在UseConstants类中直接引用Constants接口内定义的常量示例程序如下:
Java接口除上述的应用外,还有很多地方可以使用接口,例如,在Java的事件机制中就经常用到接口,再有就是对于一些已经开发好的应用系统,在结构上进行较大的调整已经是不现实的,这时可以通过定义一些接口并追加相应的实现接口的类完成功能结构的扩展。