Java核⼼技术12接⼝和抽象类
典型定义
接⼝是对⾏为的抽象,它是抽象⽅法的集合,利⽤接⼝可以达到 API 定义和实现分离的⽬的。接⼝,不能实例化;不能包含任何⾮常量成员,任何 field 都是隐含着 public static final 的意义;同时,没有⾮静态⽅法实现,也就是说要么是抽象⽅法,要么是静态⽅法。Java 标准类库中,定义了⾮常多的接⼝,⽐如 java.util.List。
抽象类是不能实例化的类,⽤ abstract 关键字修饰 class,其⽬的主要是代码重⽤。除了不能实例化,形式上和⼀般的 Java 类并没有太⼤区别,可以有⼀个或者多个抽象⽅法,也可以没有抽象⽅法。抽象类⼤多⽤于抽取相关 Java 类的共⽤⽅法实现或者是共同成员变量,然后通过继承的⽅式达到代码复⽤的⽬的。Java 标准库中,⽐如 collection 框架,很多通⽤部分就被抽取成为抽象类,例如
java.util.AbstractList。
知识拓展
Java不⽀持多继承。这种限制,在规范了代码实现的同时,也产⽣了⼀些局限性,影响着程序设计结构。Java 类可以实现多个接⼝,因为接⼝是抽象⽅法的集合,所以这是声明性的,但不能通过扩展多个
抽象类来重⽤逻辑。
设想,为接⼝添加任何抽象⽅法,相应的所有实现了这个接⼝的类,也必须实现新增⽅法,否则会出现编译错误。对于抽象类,如果我们添加⾮抽象⽅法,其⼦类只会享受到能⼒扩展,⽽不⽤担⼼编译出问题。
Java 8 以后,接⼝也是可以有⽅法实现的!
从 Java 8 开始,interface 增加了对 default method 的⽀持。Java 9 以后,甚⾄可以定义 private default method。Default method 提供了⼀种⼆进制兼容的扩展已有接⼝的办法。⽐如,我们熟知的 java.util.Collection,它是 collection 体系的 root interface,在 Java 8 中添加了⼀系列 default method,主要是增加 Lambda、Stream 相关的功能。
public interface Collection<E> extends Iterable<E> {
/**
* Returns a sequential Stream with this collection as its source
* ...
**/
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
}
⾯向对象设计
封装:⽬的是隐藏事务内部的实现细节,以便提⾼安全性和简化编程。封装提供了合理的边界,避免外部调⽤者接触到内部的细节。可能会触发更多的问题,如并发问题。
继承:是代码复⽤的基础机制。但要注意,继承可以看作是⾮常紧耦合的⼀种关系,⽗类代码修改,⼦类⾏为也会变动。在实践中,过度滥⽤继承,可能会起到反效果。
多态:你可能⽴即会想到重写(override)和重载(overload)、向上转型。多态是同⼀个⾏为具有多个不同表现形式或形态的能⼒。同⼀个接⼝使⽤不同的实例⽽执⾏不同操作。简单说,重写是⽗⼦类
中相同名字和参数的⽅法,不同的实现;重载则是相同名字的⽅法,但是不同的参数,本质上这些⽅法签名是不⼀样的。(⽅法名称和参数⼀致,返回值不同不算有效的重载,编辑会出错)
进⾏⾯向对象编程,有通⽤的⼏种基础设计原则:
单⼀职责(Single Responsibility),类或者对象最好是只有单⼀职责,在程序设计中如果发现某个类承担着多种义务,可以考虑进⾏拆分。
开关原则(Open-Close, Open for extension, close for modification),设计要对扩展开放,对修改关闭。换句话说,程序设计应保证平滑的扩展性,尽量避免因为新增同类功能⽽修改已有实现,这样可以少产出些回归(regression)问题。
⾥⽒替换(Liskov Substitution),这是⾯向对象的基本要素之⼀,进⾏继承关系抽象时,凡是可以⽤⽗类或者基类的地⽅,都可以⽤⼦类替换。
接⼝分离(Interface Segregation),接⼝的单⼀职责。我们在进⾏类和接⼝设计时,如果在⼀个接⼝⾥定义了太多⽅法,其⼦类很可能⾯临两难,就是只有部分⽅法对它是有意义的,这就破坏了程序的内聚性。
对于这种情况,可以通过拆分成功能单⼀的多个接⼝,将⾏为进⾏解耦。在未来维护中,如果某个接
⼝设计有变,不会对使⽤其他接⼝的⼦类构成影响。
依赖反转(Dependency Inversion),实体应该依赖于抽象⽽不是实现。也就是说⾼层次模块,不应该依赖于低层次模块,⽽是应该基于抽象。实践这⼀原则是保证产品代码之间适当耦合度的法宝。
OOP原则实例
public class VIPCenter {
void serviceVIP(T extend User user) {
if (user instanceof SlumDogVIP) {
// 穷 X VIP,活动抢的那种
// do somthing
java重写和重载的区别} else if(user instanceof RealVIP) {
// do somthing
}
// ...
}
改进版
public class VIPCenter {
private Map<User.TYPE, ServiceProvider> providers;
void serviceVIP(T extend User user) {
<(Type()).service(user);
}
}
interface ServiceProvider{
void service(T extend User user) ;
}
class SlumDogVIPServiceProvider implements ServiceProvider{
void service(T extend User user){
// do somthing
}
}
class RealVIPServiceProvider implements ServiceProvider{
void service(T extend User user) {
// do something
}
}
总结
1. ⽀持多重继承:接⼝⽀持;抽象类不⽀持;类不⽀持;
2. ⽀持抽象函数:接⼝语义上⽀持;抽象类⽀持;类不⽀持;
3. 允许函数实现:接⼝不允许;抽象类⽀持;类允许;
4. 允许实例化:接⼝不允许;抽象类不允许;类允许;
5. 允许部分函数实现:接⼝不允许;抽象类允许;类不允许。
6. 定义的内容:接⼝中只能包括public函数以及public static final常量;抽象类与类均⽆任何限制。
7. 使⽤时机:当想要⽀持多重继承,或是为了定义⼀种类型请使⽤接⼝;当打算提供带有部分实现的“模板”类,⽽将⼀些功能需
要延迟实现请使⽤抽象类;当你打算提供完整的具体实现请使⽤类。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论