目录
天下代码一大抄
—by 夫
1. 继承
1.1 子类型多态
逻辑上, 子类 is-a
父类.
因此在任何可以使用父类实例的场合, 都可以使用子类实例替换而不会有任何问题, 这就是子类型多态的概念.
1.2 Liskov替换原则(LSP)
为了实现子类型多态性, 子类型应满足:
- 子类型可以增加方法, 但不可删除方法
- 子类型需要实现抽象类型中的所有未实现方法
- 子类型重写的方法的返回值类型应相同或协变
- 子类型重写的方法的参数类型应相同或逆变
- 子类型重写的方法不能抛出额外的异常
- ADT的RI应相同或更强
- 方法的pre-condition应相同或更弱
- 方法的post-condition应相同或更强
LSP是子类型的特别定义, 遵循LSP的子类型叫: (强)行为子类型
协变: 子类型中某处的类是父类型该处的类的子类
逆变: 子类型中某处的类是父类型该处的类的父类
Java 对 LSP 的支持
- *
- *
- *
- Java 要求子类型重写的方法的参数类型应相同, 改变则视为重载
- Java 要求子类型重写的方法不能抛出比父类型更多的异常, 但可以协变
*: 与LSP原则要求相同
1.3 Java泛型中的继承规则
对于一个泛型类MyClass<A>
来说:
MyClass<Object>
和 MyClass<String>
都是原始类型MyClass
的子类型,
但二者之间没有任何关系, 尽管 String
是 Object
的子类型,
若 MyClass<A>
继承 MyClassFather<A>
, 则 MyClass<String>
是 MyClassFather<String>
的子类型
Java 通配符类型
通配符类型略微解决了上述问题
声明泛型对象时, 使用 ?
代表不知道
为了讨论方便, 我们假设MyClass<A>
类形如:
class MyClass<A>{
//以泛型类型做参数的方法
public void f(A a);
//以泛型类型做返回值的方法
public A g();
}
无限定通配符
不知道 ?
是什么类型
MyClass<?> mc;
不能为 ?
类型变量赋有效值(传参), 因为 ?
可表示任意类型, 所以用任何对象为它赋值都可能会出现变量类型错误的问题. 因此事实上, 只能使用 null
调用mc
的f(A)
方法.
mc.f(new Object()); //invalid
mc.f(new String()); //invalid
mc.f(null); //valid
只能将 ?
类型的实例对象赋值给 Object
型变量, 因为任何类都是 Object
类的子类.
Object o = mc.g(); //valid
子类型限定通配符
限定 ?
是某类 B
或它的子类型
MyClass<? extends Number> mc;
同样不能为 ?
型变量赋有效值;
可以将 ?
类型的实例对象赋值给 B 型及 B 型的超类型变量;
Object o = mc.g(); //valid
Number n = mc.g(); //valid
Integer i = mc.g(); //invalid
超类型限定通配符
限定 ?
是某类 B
或它的超类型
MyClass<? super Number> mc;
只能为 ?
型变量赋 B
或它的子类型的实例对象;
mc.f(new Object()); //invalid
mc.f((Number) 1); //valid
mc.f((Integer) 1); //valid
只能将 ?
类型的实例对象赋值给Object类型的变量;
Object o = mc.g(); //valid
Number n = mc.g(); //invalid
通配符类型的继承原则
把通配符想象成对所有类的全集取一个子集:
若划分的集合之间有子集关系, 则通配符类型之间有子类型关系
2. 委派
2.1 复合重用原则(CRP)
Composite Reuse Principle
类应该通过其组成(通过包含实现所需功能的其他类的实例)来实现多态行为和代码重用, 而不是从基类或父类继承.
2.2 委派类型
临时性委派(依赖)
use-a
关系的抽象
类不将另一类的实例作为字段, 而是偶尔使用另一类的实例: 有时作为参数传入, 有时在方法内部临时使用.
静态永久性委派(组成)(Composition)
is-part-of
关系的抽象, 描述一种整体与部分的关系, 整体出现, 部分同时出现; 整体消亡, 部分同时消亡.
类包含另一类的实例字段, 并且在代码中静态初始化
普通永久性委派(Association)
has-a
关系的抽象
类包含另一类的实例字段, 调用构造器时使用传入的对象对该字段进行初始化
动态永久性委派(聚合)
has-a
关系的抽象
类包含另一类的实例字段, 调用构造器时使用传入的对象对该字段进行初始化, 并可调用方法更改
2.3 库和框架
系统级别的复用
- 库: API的集合
- 白盒框架: 代码中有一些抽象方法或者只实现了默认功能的方法, 想使用这段代码时需要继承该类并重写这些方法以定制功能.
(抄卷子的时候得把名改咯) - 黑盒框架: 代码逻辑完整但需要某种接口的实例, 想使用这段代码需要自定义类以实现该接口.
(买了个打印机得配纸和墨)
*3. 实例: 给Java列表(List<E>
)排序
使用 Collections.sort(List<E>, Comparator<E>)
排序, 或使用List实例的 .sort(Comparator<E>)
方法排序.
为 E
的列表 List<E>
排序时需要 E
有某种序关系, 定义序关系可以:
- 自定义类实现
Comparator<E>
接口, 该接口有一个方法
int compare(E, E);
排序时将该类的实例传入sort
方法
- 使
E
类实现Comparable<E>
接口, 该接口有一个方法
int compareTo(E);
排序时向sort
方法传入null