JavaTutorial

接口

之前布置的三角形面积编程题中,有一道选做要求改写两个JavaBean类。这两个类的内部结构和方法实现不一样,但是它们具有相同的方法签名(除了方法体{...}以外的部分,都是方法签名)。

为什么需要接口

在上节课作业的基础(如果没有完成,可以参考课件附带的版本)上,编写一个函数,比较两个三角形的面积。第一个三角形大于第二个则输出true,否则输出false。注意,该函数必须接受至少两种三角形

我们先用三边长定义的三角形来编写函数:

static boolean compareTo(TriangleEdge tri1, TriangleEdge tri2){
    return tri1.getArea() > tri2. getArea()
}

如果需要用点线距和两边长定义的三角形来编写上述函数,那么就要添加三个不同的函数签名:

static boolean compareTo(TriangleEdge tri1, TriangleHeight tri2){/*...*/}
static boolean compareTo(TriangleHeight tri1, TriangleEdge tri2){/*...*/}
static boolean compareTo(TriangleHeight tri1, TriangleHeight tri2){/*...*/}

如果继续添加其他方式定义的三角形,就要添加更多的方法。观察一下这些方法,它们仅仅是参数类型不同,而这些类型都有getArea方法(以及JavaBean定义的其他所有方法),同时比较面积的方法只调用了getArea方法。更改参数类型的限制就变得理所当然。

static boolean compareTo(Object tri1, Object tri2){
    return tri1.getArea() > tri2. getArea()
}

上述代码必定无法通过编译,原因是Object是最宽泛的对象,不一定会有getArea方法。另一种方法是类型转换,此处不详细介绍。此时就要引入接口,来告诉面积比较函数这个参数具有getArea方法。用Triangle接口来描述具有面积的三角形:

interface Triangle{
    double getArea();

    double getBaseEdge();

    double getSecondEdge();

    double getThirdEdge();
}

class TriangleEdge implements Triangle{/*...*/}

static boolean compareTo(Triangle tri1, Triangle tri2){/*...*/}

在上面的代码中,interface关键字用于定义接口,而implements关键字说明这个类符合接口的定义(也就是类实现了接口)。定义接口后,就可以保证面积比较函数的三角形参数必然有访问面积的方法,同时不用限制三角形的构造方式。

类型层次

接口可以限制函数参数或局部变量的结构,能不能限制接口的结构?用extends关键字(扩展)来说明接口的结构,扩展接口就意味着包含另外一个接口的所有方法。如果要在四边形、三角形、圆形之间比较面积,就需要再定义一个面积接口:

// 闭合的平面形状
interface ClosedPlaneShape{
    double getArea();
}

interface Circle extends ClosedPlaneShape{
    double getRadius();
}
interface Square extends ClosedPlaneShape{
    double getBase();
}
interface Triangle extends ClosedPlaneShape{
    double getBaseEdge();

    double getSecondEdge();

    double getThirdEdge();
}

static boolean compareTo(ClosedPlaneShape tri1, ClosedPlaneShape tri2){/*...*/}

类似于现实世界的分类,implementsextends描述了对象的分类。一般把implements左边的类型称为子类,而右边的类型称为父类。

类型图

flowchart TB; ClosedPlaneShape --- Triangle; Triangle --> TriangleByEdge([TriangleByEdge]) & TriangleByHeight([TriangleByHeight]); ClosedPlaneShape --- Circle; ClosedPlaneShape --- Square;

类型图能用来来描述分类关系。这个类型图描述了能够比较面积的平面形状。从父类分出几个分支,而这些分支就是子类。

对于本课程涉及的类型图而言,父类在上子类在下。方框代表类,而圆框代表接口。

接口的文档

之前我们提到过文档注释的语法。只要某个程序包(软件、库、IP核)被复用,那么就需要文档来说明,这个程序包提供了什么功能。文档需要说明,这个程序包包含哪些函数,每一个函数接受什么样的输入,会产生什么现象,反馈给调用者的又是什么结果,能否重复调用等等。

传统的做法是,将文档和程序分开编写,文档存储在专门的文件里,尽管这种方法便于导出用户手册,但是无法对照着看文档和程序,修改程序的同时也不容易更新文档,会给目前(2022年)的版本控制系统带来麻烦。本节课会提到文档注释和javadoc,这种方法是把文档作为函数等的注释,同时写在代码文件里,这种方法可以同时记录代码和文档的改动,但是非常难处理复杂的格式。

文档注释

文档注释是特殊的注释,这种注释通常写在变量、函数、类等的定义前方。文档注释里有一系列特殊的标记:

/**
 * 比较两个三角形的面积,第一个三角形面积较大则返回true,反之返回false
 * @param tri1 第一个三角形
 * @param tri2 第二个三角形
 * @return
 * @throws AreaException 三角形三边关系不满足三角形条件时抛出
 */
static boolean compareTo(Triangle tri1, Triangle tri2){/*...*/}

javadoc是Java默认的文档生成程序,会根据文档内的特殊标签来扫描注释,然后生成HTML格式的文档。其他工具也会识别这些特殊标签。

程序包的文档

如果某个包具有相似的特征,就可以添加包文档。包文档位于package-summary.java文件内:

/**
 * 测试流的例程
 */
package stream;