在前文策略+工厂模式实践中,我使用了一个typescript interface Convertor来约束每个具体转换器必须实现的方法,然而在刘老师的例子中,他使用的是Java中的抽象类,让具体类来继承抽象类,也达到了同样的目的。当时,我脑海中就浮现了两个问题:
- javascript有抽象类吗?
- 如果有的话,抽象类和接口有什么区别?这些区别同样在Java 语言中体现吗?
- 如果没有的话,当出现需要使用abstract的场景时该如何处理?
首先来回答第一个问题:js中有抽象类吗?
准确的说,没有。但是在js的超集Typescript中有abstract特性, 那么,可以得知interface和abstract一样,在原生JS中不支持,但是TS中支持。
接下来回答第二个问题:抽象类和接口有什么区别?
经过查阅相关资,对比分析下来,共发现3个区别:
- 一个类可以实现多个接口,但是不能继承多个类(TS可以使用Mixin达到同样效果)
- 使用抽象类可以在运行时做类型检测,而使用接口时不行
- 抽象类中可以包含非抽象的函数成员,而接口针对函数,只能定义函数名,参数及返回值类型
而为什么我在上文中使用Interface而不是用抽象类?原因有三个:
- 仅仅需要约束函数名参数及返回值类型,不需要额外的函数实现
- 不需要在运行时判断一个类是否是Convertor的实例
- 使用接口更简单,占用资源更小
既然Java和TS都有抽象类和接口,那上述三个区别是不是也同样体现在Java中呢?
经过查阅相关文档,发现第1,3点区别和JS是相同的;
其中第二点是JS特有的。JS是弱类型语言,我们经常需要在运行时判断数据类型,常见的操作有使用typeof, instanceOf, isArray等;而Java是强类型的语言,在静态编译阶段就可以确定数据的类型,(一般不会在运行时在判断类型?),就算需要判断其类名,也可以直接使用.getClass().getSimpleName();
来实现。
除此之外,对于Java语言来说,abstract类和interface之间还有几个独特于js的区别,分别是:
- 抽象类可以定义final, non-final, static, non-static的变量,而Interface只能定义static, final的变量
- 抽象类中可以包含以procted, private包含的成员,而接口中只能以Public修饰。
JS中,interface的成员无法用任何修饰词修饰,所以不存在以上两个Java语言的特征。
最后,回答文章开头的第三个问题:当出现需要使用abstract的场景,又没有TS的时,该如何处理?
首先要搞清楚,abstract有什么特性以及使用场景是什么。
An abstract method or abstract field is one that hasn’t had an implementation provided. These members must exist inside an abstract class, which cannot be directly instantiated.
The role of abstract classes is to serve as a base class for subclasses which do implement all the abstract members. When a class doesn’t have any abstract members, it is said to be concrete.
---------出自TS官方文档 https://www.typescriptlang.org/docs/handbook/2/classes.html#abstract-classes-and-members
由此而知,abstrac类的特性:
- 不能被实例化,只能被继承;
- 抽象的属性,方法只能存在于抽象类中;
适用场景:
多个类中存在相似的属性和行为,把它们抽象出来,成为一个基类,但是这个基类和普通父类不一样的是,它的作用仅限于被子类继承,实例化其本身是没有意义的。
从以上分析可以得知,实现抽象类的最核心的要点就是让其禁止被实例化。在ES5中可以这样做:
function Base(age){
this.age = age
if(this.constructor === Base){
throw Error('err')
}
}
function Sub(age){
Base.call(this, age)
}
Sub.prototype = new Base() // show error
在ES6中可以这样做:
class Base{
constructor(){
if(this.constructor === Base){
throw new Error("Abstract class cannot be initialized") }
}
}
new Base() // show error
如今TS问世,可以不再采用原来hack的方式,转而用原生的写法:
abstract class Convertor{
abstract digitalize(){
}
abstract visualize(){
}
}
class ExcelConvertor extends Convertor{
digitalize(){
}
visualize(){
}
}
参考链接
Difference between Abstract Class and Interface in Java
网友评论