2-2-接口与抽象类

上一级:系统整理java知识体系

接口的定义及实现 interface

接口(interface)用来描述类应该做什么,而不知道具体的实现方法,将这些具体方法放在子类中加以实现。

一个类可以实现(implement)一个或者多个接口。

接口可以在不同的类中分别有不同的具体实现。

接口中的所有方法都默认是自动是public方法,你不要再写修饰符public。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public interface Comparable<T> {
/**
* Compares this object with the specified object for order. Returns a
* negative integer, zero, or a positive integer as this object is less
* than, equal to, or greater than the specified object.
*
* <p>The implementor must ensure
* {@code sgn(x.compareTo(y)) == -sgn(y.compareTo(x))}
* for all {@code x} and {@code y}. (This
* implies that {@code x.compareTo(y)} must throw an exception iff
* {@code y.compareTo(x)} throws an exception.)
*
* <p>The implementor must also ensure that the relation is transitive:
* {@code (x.compareTo(y) > 0 && y.compareTo(z) > 0)} implies
* {@code x.compareTo(z) > 0}.
*
* <p>Finally, the implementor must ensure that {@code x.compareTo(y)==0}
* implies that {@code sgn(x.compareTo(z)) == sgn(y.compareTo(z))}, for
* all {@code z}.
*
* <p>It is strongly recommended, but <i>not</i> strictly required that
* {@code (x.compareTo(y)==0) == (x.equals(y))}. Generally speaking, any
* class that implements the {@code Comparable} interface and violates
* this condition should clearly indicate this fact. The recommended
* language is "Note: this class has a natural ordering that is
* inconsistent with equals."
*
* <p>In the foregoing description, the notation
* {@code sgn(}<i>expression</i>{@code )} designates the mathematical
* <i>signum</i> function, which is defined to return one of {@code -1},
* {@code 0}, or {@code 1} according to whether the value of
* <i>expression</i> is negative, zero, or positive, respectively.
*
* @param o the object to be compared.
* @return a negative integer, zero, or a positive integer as this object
* is less than, equal to, or greater than the specified object.
*
* @throws NullPointerException if the specified object is null
* @throws ClassCastException if the specified object's type prevents it
* from being compared to this object.
*/
int compareTo(T o);
}

接口中绝不会有实例字段,在Java8之前,接口中绝对不会有默认的实现方法。java8之后可以有简单的方法,但这些方法不能引用实例字段,因为接口没有实例。

提供实例字段和具体方法的任务一个交由实现接口的那个类来完成。

接口的属性

接口不是类,具体来说,不能使用new运算符来实例化一个接口;

但是可以声明接口的变量,但其必须引用实现了这个接口的类对象

可以使用instanceof来检查有关对象是否实现了某个特定的接口

与建立类的继承层次一样,也可以对接口进行扩展。允许有多条接口链,从通用性高的接口逐步扩展到专用性较高的接口

接口中不能包含实例字段,但是却可以包含常量(static final)。

接口中的字段总是public static final的,这些修饰符可以省略

  • Java语言规范建议我们不要提供多余的关键词,即可以省略的标识符就不用再写出来。

Java中每个类只能有一个超类,却可以实现多个接口

例如可以让Employee类实现Cloneable和Comparable两个接口,语句如下

1
2
3
class Employee implements Cloneable,Comparable{
...
}

这样Employee类在实现这两个接口后,就可以拥有克隆和比较的能力

默认方法

默认方法常用来实现接口的演化,在接口中方法前添加default修饰符,然后就能对这个方法提供一个默认的实现。

有一个Bag类,它是在很久以前的Collection接口上实现的。

后来Collection接口新增了一个stream方法,怎么让以前的Bag类编译不出错呢?(毕竟编写这个类的年代还不存在这个方法,Bag类根本不可能提供一个stream方法的具体实现)

可以把在Collection接口中的stream方法实现为默认方法,即如果子类不实现它,自动调用接口中默认的Collection.stream实现。

要注意的是,为接口增加一个非默认方法不能保证“源代码兼容”。

为接口增加方法可以保证“二进制兼容”.即原先包含了Bag类的一个JAR文件,这个很老的Bag类依然能够正常加载,尽管它没有实现stream方法。程序仍然可以正常的构造Bag实例,

  • 只不过在Bag实例上调用stream方法会报出AbstractMethodError

默认方法冲突、二义性

如果至少有一个接口提供了一个实现,那么接口间出现冲突时,编译器会报告错误,程序员必须解决这个二义性

如果两个接口都没有为共享方法(同名同参数的方法)提供默认实现,那么就不存在冲突,而是子类在implement这两个接口时给出一个具体实现就好了。亦或者干脆不去实现这个方法,子类本身也成为抽象类。

抽象类的定义及实现

  • C++抽象中实现抽象类的方法叫做纯虚函数,,将类中一个方法标记为virtual …(函数名和参数) = 0;存在这样至少一个纯虚函数的类就是抽象类.

    这是因为C++中没有提供标识出一个类是抽象类的关键字

祖先类更有一般性,人们只将它作为派生其他类的基类,而不是用来构造你想使用的特定的实例。

例如,员工是一个人,而学生也是一个人,那么就可以扩展类层次结构,加入他们的共同超类Person。在其中添加一些更通用性,更一般性的方法

  • 建议将通用的字段和方法(不管其是否抽象)放在超类中(无论其是否是抽象类)

为了提高程序的清晰度,包含一个或者多个抽象方法的类本身必须被声明为抽象的。

使用abstract关键字后,就完全不需要实现这个办法了,因为你将实现这个办法的任务交给了它的子类

1
2
3
4
5
public abstract class person{
...
public abstract String getDescription();
//no implementation(实现) required
}

抽象类也可以包含字段和具体方法

Person类中保存了一个人的姓名,以及一个返回姓名的具体方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package abstractClasses;

/**
* This program demonstrates abstract classes.
*
* @author Cay Horstmann
* @version 1.01 2004-02-21
*/
public abstract class person {
public abstract String getDescription();

private final String name;

public person(String name) {
this.name = name;
}

public String getName() {
return name;
}
}
  • 通用的方法和通用的字段最好放在超类中

抽象方法只是提供给你一个未具体实现的占位方法。

扩展抽象类,可以在子类中保留抽象类的一部分,即一部分或者全部的抽象方法仍未定义。这时必须把这个子类也标记为抽象类。

另外一种做法是把子类中所有的抽象方法都具体的实现,这样子类就不用再定义成抽象类了。

即使不含抽象方法,也可以将类声明为抽象类

抽象类不能实例化,一个类声明为abstract后,就不能再创建这个类的对象实例。

但其子类如果不再抽象,就可以创建一个子类的对象

可以创建一个抽象类的对象变量,但这个变量只能引用其子类中非抽象类对象

Java不支持多重继承,而是提供了有其大多数好处的接口

使用抽象类表示通用属性存在一个严重的问题,即每个类只能扩展(extend)一个类,Employee已经extend了Person类,它就无法扩展第二个类了,因为Java不支持多继承,

所以Java使用了提供类似多重继承功能,但要简单(与C++相比)与高效(与Eiffel相比)的多的接口(interface)