Java with OOP: 可复用性

天下代码一大抄
—by 夫

1. 继承

1.1 子类型多态

逻辑上, 子类 is-a 父类.
因此在任何可以使用父类实例的场合, 都可以使用子类实例替换而不会有任何问题, 这就是子类型多态的概念.

1.2 Liskov替换原则(LSP)

为了实现子类型多态性, 子类型应满足:

  1. 子类型可以增加方法, 但不可删除方法
  2. 子类型需要实现抽象类型中的所有未实现方法
  3. 子类型重写的方法的返回值类型应相同或协变
  4. 子类型重写的方法的参数类型应相同或逆变
  5. 子类型重写的方法不能抛出额外的异常
  6. ADT的RI应相同或更强
  7. 方法的pre-condition应相同或更弱
  8. 方法的post-condition应相同或更强

LSP是子类型的特别定义, 遵循LSP的子类型叫: (强)行为子类型
协变: 子类型中某处的类是父类型该处的类的子类
逆变: 子类型中某处的类是父类型该处的类的父类
没啥可描述的


Java 对 LSP 的支持

  1. *
  2. *
  3. *
  4. Java 要求子类型重写的方法的参数类型应相同, 改变则视为重载
  5. Java 要求子类型重写的方法不能抛出比父类型更多的异常, 但可以协变

*: 与LSP原则要求相同

1.3 Java泛型中的继承规则

对于一个泛型类MyClass<A>来说:
MyClass<Object>MyClass<String> 都是原始类型MyClass的子类型,
但二者之间没有任何关系, 尽管 StringObject 的子类型,
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 调用mcf(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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值