一、缘起
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待研究场景
-
factory
用于单例模式的创建。
class Person {
factory Person() => _instance;
Person._internal();
static final Person _instance = Person._internal();
}
-
Dio
中的思路
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.