一、缘起
JJ 最近在在使用Flutter 重构以前Native 项目时,对网络库dio 进行封装的时候发现Dio 在实例的时候是 Dio 对象时,无意中发现Dio class 时抽象类,却还能使用Dio() 构造函数实例化,这是为什么呢?查看Flutter 源码时集合对象List , Map 定义时都是abstract class 震惊!!! why?
源于factory 工厂构造函数。
二、关于dart 中class 的声明
1.举个🌰
示例采用 官方文档 和文档描述,因文档和blog众多,此处不再赘述。
class Logger {
final String name;
bool mute = false;
static final Map<String, Logger> _cache = <String, Logger>{};
factory Logger(String name) {
return _cache.putIfAbsent(name, () => Logger._internal(name));
}
factory Logger.fromJson(Map<String, Object> json) {
return Logger(json['name'].toString());
}
Logger._internal(this.name);
void log(String msg) {
if (!mute) print(msg);
}
}
上述代码出现的factory会在下面一起描述
三、abstract class 抽象类
3.1 dart 中的抽象类不能实例化?
答案是肯定的,和其他语言一样抽象类是不能实例化.
那下面代码该如何解释呢?
Map<String, dynamic> map = Map();
追踪一下Map 的定义
// from: DartPackage/sky_engine/core/main.dart
...
/// Creates an empty [LinkedHashMap].
abstract class Map<K, V> {
external factory Map();
...
}
Map 定义的修饰符是abstract ,为什么可以通过构造函数实例化?- 和
Map 类似的还有List , 此处以Map 为例 - 此处的
external 和 facotry 又是啥意思,是他们影响的吗?顺着这个线索我们去一探究竟。
3.2 刨根问底
3.2.1 external
9.4 External Functions externalFunctions An external function is a function whose body is provided separately from its declaration. An external function may be a top-level function (19), a method (10.2, 10.8), a getter (10.3), a setter (10.4), or a non-redirecting constructor (10.7.1, 10.7.2). External functions are introduced via the built-in identifier external (17.38) followed by the function signature. 文档索引
JJ 的理解:external :编译标识,关键字主要作用是:将声明和实现分开。external 函数呢可能是一个顶级函数,或者方法,或者一个getter , 一个setter 又或者非重定向的构造函数 。巴拉巴拉这么多,表明这个关键字 是用来定义声明 的。后面呢主要是说明,是用来解决 dart.vm 在浏览器和客户端和服务端平台的不同实现而引入的。我这个又是废话了,嗯,那我们来顺着这个线索来找一下这个Map 的body (实现)!
3.2.1 body
根据线索,我们知道了,Map() 创建了一个/// Creates an empty [LinkedHashMap]. 然后我们就从 LinkedHashMap 入手,还有文档提供的这是关于dart.vm 来的,所以只能去瞅瞅sdk 了。IDE 打开sdk 全局搜索吧。
万幸搜索的结果不是很多,就一个一个的去看吧,先到第一个文件中去看。篇幅有限,只截取代码片段,
@patch
class LinkedHashMap<K, V> {
@patch
factory LinkedHashMap(
...
if (isValidKey == null) {
if (hashCode == null) {
if (equals == null) {
return new _InternalLinkedHashMap<K, V>();
}
hashCode = _defaultHashCode;
} else {
if (identical(identityHashCode, hashCode) &&
identical(identical, equals)) {
return new _CompactLinkedIdentityHashMap<K, V>();
}
...
- 此处我们知道了,LinkedHashMap 是返回了
_InternalLinkedHashMap 和 _CompactLinkedIdentityHashMap 对象。
3.2.2 @Path 为何物?
path 的主要功能是和external 关联的注解。
3.2.3 最终的搜索范围缩小到上述的两个对象
目录为 ur flutter sdk path/bin/cache/dart-sdk/lib/_internal/vm/bin/compact_hash.dart
// VM-internalized implementation of a default-constructed LinkedHashMap.
@pragma("vm:entry-point")
class _InternalLinkedHashMap<K, V> extends _HashVMBase
with
MapMixin<K, V>,
_HashBase,
_OperatorEqualsAndHashCode,
_LinkedHashMapMixin<K, V>
implements LinkedHashMap<K, V> {
_InternalLinkedHashMap() {
_index = _uninitializedIndex;
_hashMask = _HashBase._UNINITIALIZED_HASH_MASK;
_data = _uninitializedData;
_usedData = 0;
_deletedKeys = 0;
}
- 由此可以说明:
abstract 抽象类中 factory 构造函数返回的instance 实例,是来自于实现类 (暂且将abstract 类看作interface ,dart 中并不包含interface 关键字).
3.2.4 _CompactLinkedIdentityHashMap
探索过程同理.
3.3 总结
3.3.1抽象类实构造函数实例化的原因
由于抽象类factory 工厂构造函数的作用,返回了实现类实例的实例。
官方文档描述的:factory 构造函数中返回instance 实例并不是都是从本实现类创建新的,可能来自于缓存,也就是章节二中表示的;也可能来自于子类。
3.3.2待研究场景
class Person {
factory Person() => _instance;
Person._internal();
static final Person _instance = Person._internal();
}
dio 库代码Dio 的设计思路。
- 此处工厂构造函数的实例对象类型有两个
DioForBrowser ,DioForNative - 为了
移动端 和网页端 的支持用了比较巧妙的入口判断 详情请参考注释部分 - 多端支持的实例化来来自于此dart-clang-issue;
...
// 入口判断代码
import 'entry_stub.dart'
if (dart.library.html) 'entry/dio_for_browser.dart'
if (dart.library.io) 'entry/dio_for_native.dart';
abstract class Dio {
factory Dio([BaseOptions? options]) => createDio(options);
....
}
logs
- jeversonjee 22.07.02 3.3.4.
|