接口(Interfaces)
基本概念不再赘述,值得注意(记住)的有几点:
- 接口不应该有构造函数
- 只有静态方法才能拥有方法体
- 接口实现不能破坏表示不变性
- 接口实现可以添加接口没有的方法,但是不能缺少接口声明的方法
- 接口实现不应该违背接口规约(比如接口规约声明为不可变类型,但在实现类里加入了mutator)
子类型(subtype)
a是b的子类型,则a要么implement b,要么extend b。
a是b的子类(subclass),则a extend b。
(随着接口的提出,java似乎是第一个跳出来说出子类型与子类是有区别的语言,并在某种意义上不鼓励继承,而推崇接口与实现。关于子类和子类型的区别笔者还理解不够,读者大可选择无视)
思考一个问题,ArrayList是可变的,List.of()是不可变的,它们都是List的合法子类型,那么List到底是可变还是不可变的呢?
答案是模糊的,事实上List、Map、Set等接口的规约中对此接口ADT到底是可变还是不可变的并没有明确的声明。同时要注意的是,子类型的规约强度应高于产生子类型的类型的规约强度(precondition变弱或postcondition变强)
泛型(Generic Types)
泛型的提出让java设计的重用性变得更上一层楼,它与接口相互结合,让java拥有了自己的特色。
接口的好处
- 接口可以让编译器帮忙检查ADT(实现类)的明显错误
- 提供多种功能,不同实现方法在不同操作上可能效率不同
子类(Subclass)与继承
继承父类与实现接口的区别在于rep也会从父类传到子类,但是非严格继承是容易产生错误的(此部分原文没有)
- final对继承的影响:
final修饰父类方法,子类无法重写
final修饰父类,不能继承
- 非严格继承:子类中重写了父类的方法(难保证语义没有改变)
辨析下面的例子
运行下面的代码,结果分别是什么?
第一块:正确
第二块:正确,因为Horse中有来自父类的eat()方法
第三块:正确
第四块:正确
第五块:错误
第六块:错误!ah2虽然是指向Horse的引用,但本身是Animal类,并没有eat(String s)方法
初学者很容易出现第第六块的错误,解决方法如下:
1、直接用Horse类声明,就像第四块
2、向下转型,原则上第六块的错误是向上转型导致的
3、在Animal类中声明eat(String s)方法
但如果是重写eat方法
调用的却是Horse中的eat方法