本章的主要内容是接口,由于我们学习的软件构造是用Java语言编写的,所以我们这里的接口是指Java语言的接口。
接口(软件类接口)是指对协定进行定义的引用类型。其他类型实现接口,以保证它们支持某些操作。接口指定必须由类提供的成员或实现它的其他接口。与类相似,接口可以包含方法、属性、索引器和事件作为成员。
而在Java中:Java里面由于不允许多重继承,所以如果要实现多个类的功能,则可以通过实现多个接口来实现。
Java接口和Java抽象类代表的就是抽象类型,就是我们需要提出的抽象层的具体表现。OOP面向对象的编程,如果要提高程序的复用率,增加程序的可维护性,可扩展性,就必须是面向接口的编程,面向抽象的编程,正确地使用接口、抽象类这些太有用的抽象类型做为java结构层次上的顶层。
Java接口和Java抽象类有太多相似的地方,又有太多特别的地方,究竟在什么地方,才是它们的最佳位置呢?把它们比较一下,你就可以发现了。
1)Java接口和Java抽象类最大的一个区别,就在于Java抽象类可以提供某些方法的部分实现,而Java接口不可以,这大概就是Java抽象类唯一的优点吧,但这个优点非常有用。如果向一个抽象类里加入一个新的具体方法时,那么它所有的子类都一下子都得到了这个新方法,而Java接口做不到这一点,如果向一个Java接口里加入一个新方法,所有实现这个接口的类就无法成功通过编译了,因为你必须让每一个类都再实现这个方法才行,这显然是Java接口的缺点。
2)一个抽象类的实现只能由这个抽象类的子类给出,也就是说,这个实现处在抽象类所定义出的继承的等级结构中,而由于Java语言的单继承性,所以抽象类作为类型定义工具的效能大打折扣。在这一点上,Java接口的优势就出来了,任何一个实现了一个Java接口所规定的方法的类都可以具有这个接口的类型,而一个类可以实现任意多个Java接口,从而这个类就有了多种类型。
3)从第2点不难看出,Java接口是定义混合类型的理想工具,混合类表明一个类不仅仅具有某个主类型的行为,而且具有其他的次要行为。
1)接口只为使用者提供“契约”(contract),而使用者只需要读懂这个接口即可使用该ADT,他也不需要依赖ADT特定的实现/表示,因为实例化的变量不能放在接口中(具体实现被分离在另外的类中)。
2)允许一种抽象类型能够有多种实现/表示,即一个接口可以有多个实现类(一个类也可以同时实现多个接口)。而当一个类型只用一个类来实现时,我们很难改变它的内部表示。
Java的静态检查会发现没有实现接口的错误,例如,如果程序员忘记实现接口中的某一个方法或者返回了一个错误的类型,编译器就会在编译期报错。不幸的是,编译器不会去检查我们的方法是否遵循了接口中的文档注释。
当类实现接口的时候,类要实现接口中所有的方法。否则,类必须声明为抽象的类。
类使用implements
关键字实现接口。在类声明中,implements
关键字放在class
声明后面。
实现一个接口的语法,可以使用这个公式:
...implements 接口名称[, 其他接口名称, 其他接口名称..., ...] ...
}
接口实现实例:
public class SimpleMyString implements MyString {
private char[] a;
/** Create a string representation of b, either "true" or "false". *
@param b a boolean value */
public SimpleMyString(boolean b) {
a = b ? new char[] { 't', 'r', 'u', 'e' }
: new char[] { 'f', 'a', 'l', 's', 'e' };
}
// private constructor, used internally by producer operations
private SimpleMyString(char[] a) {
this.a = a;
}
@Override public int length() { return a.length; }
@Override public char charAt(int i) { return a[i]; }
@Override public MyString substring(int start, int end) {
char[] subArray = new char[end - start];
System.arraycopy(this.a, start, subArray, 0, end - start);
return new SimpleMyString(subArray);
}
}
写接口中声明的方法时,需要注意以下规则:
1.类在实现接口的方法时,不能抛出强制性异常,只能在接口中,或者继承接口的抽象类中抛出该强制性异常。
2.类在重写方法时要保持一致的方法名,并且应该保持相同或者相兼容的返回值类型。
3.如果实现接口的类是抽象类,那么就没必要实现该接口的方法。
在实现接口的时候,也要注意一些规则:
1.一个类可以同时实现多个接口。
2.一个类只能继承一个类,但是能实现多个接口。
3.一个接口能继承另一个接口,这和类之间的继承比较相似。
在Java代码中,接口被用的很广泛,下面是几个使用接口的好处:
1.接口对于编译器和读者来说都是重要的文档:接口不仅会帮助编译器发现ADT实现过程中的错误,它也会帮助读者更容易/快速的理解ADT的操作——因为接口将ADT抽象到了更高的层次,用户不需要关心具体实现的各种方案。
2.允许进行性能上的权衡:接口使得ADT可以有不同的实现方案,而这些实现方案可能在不同环境下的性能或其他资源特性有很大差别。使用者可以根据自己的环境/需求选择合适的实现方案。但是,在我们选择特定的方案后,我们依旧要保持代码的表示独立性,即当ADT发生(内部)改变或更换实现方案后代码依然能正常运行。
3.通过未决定的规格说明给实现者以定义方法的自由:例如,当把一个有限集合转化为一个列表的时候,有一些实现可能是使用较慢的方法,但是它们确保这些元素在列表中是排好序的;而其他的实现可能是不管这些元素转换后在列表中的排序,但是它们的速度更快。
4.一个类具有多种“视角”:在Java中,一个类可以同时实现多个接口,例如,一个能够显示列表的窗口部件就可能是一个同时实现了窗口和列表这两个接口的类。这反映的是多种ADT特性同时存在的特殊情况。
5.允许不同信任度的实现:另一个多次实现一个接口的原因在于,你可以写一个简单但是非常可靠的实现,也可以写一个很“炫”但是bug存在的几率(稳定性)高一些的实现。而使用者可以根据实际情况选择相应的方案。