1 Dart对象扩展
1.1 extension
1.1.1 介绍
可以在不更改类或创建子类的情况下,向类添加扩展功能的一种方式。灵活使用 extension 对基础类进行扩展,对开发效率有显著提升。
1.1.2 需求
在开发项目中碰到需求:将单位为分的数值转换成单位为元的字符串
1.1.3 常规示例
///通常的写法,封装转换方法
///封装方法:金额转字符串 保留两位小数
String convertPointToUnit(int num){
return (num.toDouble() / 100).toStringAsFixed(2);
}
///使用
void main(){
int num = 100;
var result = convertPointToUnit(num);
print(result); //打印结果为 1.00
}
1.1.4 Dart扩展
/// 使用 extension 对 int 类进行扩展,添加方法 moneyString
extension ExInt on int {
/// 金额转字符串 保留两位小数
/// 100 => 1.00
String get moneyString => (this.toDouble() / 100).toStringAsFixed(2);
}
import ../ExInt.dart;
///使用
void main(){
int num = 100;
print(num.moneyString);
}
1.1.5 各种场景的扩展演示
1、对枚举进行扩展实现
enum FruitEnum { apple, banana }
extension ExFruitEnum on FruitEnum {
String get name {
switch (this) {
case FruitEnum.apple:
return "apple";
case FruitEnum.banana:
return "banana";
}
}
}
///字符串匹配枚举
FruitEnum generateFruit (String fruitType){
if(fruitType == FruitEnum.apple.name){
return FruitEnum.apple;
} else if(fruitType == FruitEnum.banana.name){
return FruitEnum.banana;
}
}
2、扩展作用于泛型
//扩展list的方法
extension ExList<T> on List<T> {
//扩展操作符
List<T> operator -() => reversed.toList();
//一个链表分割成两个
List<List<T>> split(int at) => <List<T>>[sublist(0, at), sublist(at)];
}
3、Widget 控件中的应用
原型
Column(
children: <Widget>[
Container(paddint: const EdgeInsets.all(10) child: AWidget(),),
Container(paddint: const EdgeInsets.all(10) child: BWidget(),),
Container(paddint: const EdgeInsets.all(10) child: CWidget(),),
]
)
扩展在 Widget 控件中的应用 我们会有类似的控件
扩展
extension ExWidget on Widget { Widget paddingAll(double padding) { return Container( paddint: const EdgeInsets.all(padding) child: this, ); } }
Column( children: <Widget>[ AWidget().paddingAll(10), BWidget().paddingAll(10), CWidget().paddingAll(10), ] )
1.2 Call
1.2.1 说明
如果类实现了call()方法,则该类的对象可以作为方法使用
1.2.2 示例
void main() {
Person person = new Person();
person.name = "Tom";
person.age = 10;
var result = person("Test", 20);
print("result===>$result");
}
class Person {
String name;
int age;
int call(String name, int age) {
print("Name is $name,Age is $age");
return 3;
}
}
1.3 NoSuchMethod
1.3.1 解析
一般应用为 接口 - 实现类中使用,用来动态检测接口相关方法是否成功重写!
在访问不存在的方法或属性时调用。
动态成员调用可以尝试调用接收对象上不存在的成员
1.3.2 例子
dynamic object = 1;
object.add(42); // Statically allowed, run-time error
此无效代码将调用整数1 的noSuchMethod 方法,其中Invocation 表示.add(42) 调用和参数(然后抛出)。
类可以覆盖 noSuchMethod 以为此类无效动态调用提供自定义行为
1.3.3 具有非默认 noSuchMethod 调用的类也可以省略其接口成员的实现
class MockList<T> implements List<T> {
noSuchMethod(Invocation invocation) {
log(invocation);
super.noSuchMethod(invocation); // Will throw.
}
}
void main() {
MockList().add(42);
}
1.3.4 说明
即使MockList 类没有任何List 接口方法的具体实现,此代码也没有编译时警告或错误。对 List 方法的调用被转发到 noSuchMethod ,因此此代码将 log 调用类似于 Invocation.method(#add, [42]) 然后抛出。
如果从 noSuchMethod 返回一个值,它将成为原始调用的结果。如果值不是原始调用可以返回的类型,则调用时会发生类型错误。
默认行为是抛出 NoSuchMethodError 。
2 包管理
2.1 关键字library 和 import
2.1.1 库的引用:package: scheme
// Importing core libraries。
import 'dart:math';
// Importing libraries from external packages
import 'package:test/test.dart';
// Importing files
import 'path/to/my_other_file.dart';
2.1.2 引入的时候,创建库的prefix,关键字as
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
// Uses Element from lib1.
Element element1 = Element();
// Uses Element from lib2.
lib2.Element element2 = lib2.Element();
2.1.3 限制性导入(即导入库中的一部分),关键字show/hide
// Import only foo.
import 'package:lib1/lib1.dart' show foo;
// Import all names EXCEPT foo.
import 'package:lib2/lib2.dart' hide foo;
2.1.4 延迟加载(Lazily loading a library),一般用于web端,关键字deferred as
// 可以异步使用
import 'package:greetings/hello.dart' deferred as hello;
Future<void> greet() async {
await hello.loadLibrary();
hello.printGreeting();
}
异步、多次使用库方法,库文件只会被加载一次。
Here are some cases when you might use deferred loading:
1、To reduce a web app’s initial startup time.
2、To perform A/B testing—trying out alternative implementations of an algorithm, for example.
3、To load rarely used functionality, such as optional screens and dialogs.
Keep in mind the following when you use deferred loading:
1A deferred library’s constants aren’t constants in the importing file. Remember, these constants don’t exist until the deferred library is loaded.
2、
You can’t use types from a deferred library in the importing file. Instead, consider moving interface types to a library imported by both the deferred library and the importing file.
3、Dart implicitly inserts loadLibrary() into the namespace that you define using deferred as namespace. The loadLibrary() function returns a Future.
2.2 库的实现(Implementing libraries)
2.2.1 库必要结构
图例
说明
配置文件:pubspec.yaml
库目录:lib
在lib目录下的文件,默认是对外公开的。
实践中,一般将源码放在lib/src,src下文件私有;对外文件,需要使用export额外导出
案例
图例
示例
其中,对外的文件有shelf_io.dart 和 shelf.dart 两个文件。
export 'src/cascade.dart' show Cascade;
...
export 'src/middleware/add_chunked_encoding.dart' show addChunkedEncoding;
...
2.2.2 条件导入和导出,Conditionally importing and exporting library files
示例
// lib/hw_mp.dart
export 'src/hw_none.dart' // Stub implementation
if (dart.library.io) 'src/hw_io.dart' // dart:io implementation
if (dart.library.html) 'src/hw_html.dart'; // dart:html implementation
说明
上面代码的意思:
1、当import 'dart:io';时,导出文件src/hw_io.dart。
2、当import 'dart:html';时,导出文件src/hw_html.dart。
3、其他,导出文件src/hw_none.dart。
2.2.3 包,packages
1、说明
包管理遵循pub package manager。
包存储:公网 pub.dev site、本地、私有服务器。
包管理工具
1、命令行工具dart pub
2、IDE支持插件
2、包的创建
创建配置文件 pubspec.yaml,文件格式
包的获取,Getting packages
获取包命令:dart pub get
包引用配置package_config.json:支持配置包的不同的url地址
3、从包里面引用库,Importing libraries from packages
import 'package:js/js.dart' as js; import 'package:intl/intl.dart';
4、transmogrify包
transmogrify/ lib/ transmogrify.dart parser.dart test/ parser/ parser_test.dart
import 'package:transmogrify/parser.dart';
5、总结
```
import 导包
as a 指定区分文件
show 类、属性、函数 表示:只导入XXX
hide 类、属性、函数 表示:只有当前XXX不导入,其余全部导入
export 相当于C里面的文件融合的概念 公开这个文件
library lib; 等同于java中的package
```
2.2.4 pub
Dart采用pub管理包,介绍几个常用的pub命令。
1、库地址
https://pub.dev/packages
私有库安装
https://blog.csdn.net/ameryzhu/article/details/101688994
发布资料
http://www.icodebang.com/article/313074
https://blog.csdn.net/tp7309/article/details/104758960/
2、pub get
获取包依赖。具体是指获取在pubspec.yaml文件中dependencies目录下指定的依赖的包。在实际运行时,pub会先到pub cache目录下该依赖项是否存在,不存在才会去获取。默认情况下,Pub 会创建一个 .packages 文件用于映射 Package 名到位置 URI。在创建 .packages 文件之前,Pub 常常还会创建一个 packages 目录。
pub get 命令获取新依赖项后会写入一个 lockfile 文件以确保下次执行该命令时会使用相同的依赖项版本。应用型的 Package(即Application package) 应该总是签入该 lockfile 文件以控制来源,从而确保在将 Package 部署到生产环境时所有的依赖项对于所有开发者而言都是相同的版本。库类型的 Package(即Libraries package) 则不需要签入 lockfile 文件,因为它们可能需要使用到不同的依赖项版本。
获取完毕后,就可以在代码中通过import命令导入并使用这些包了。
当更新pubspce文件中的包依赖时(包括更新、增加、删除等),运行pub get命令,会自动更新相关内容。现在的IDE一般都不需要手动运行该命令了,比如VS CODE的dart插件,每次更新并保存pubspec文件时,会自动运行该命令。
如果没有网络、或者你不想 Pub 去线上检查,可以使用 --offline 命令参数让该命令在离线模式下执行。在离线模式下,Pub 只会从本地缓存区查找已经下载到的可用 Package。
3、pub run
使用该命令可以从命令行运行一个位于你 Package 中或 Package 依赖项中的脚本。注意,该命令只能运行位于你当前package或其依赖项中的脚步。如果需要运行其它package中的脚步,需要使用 global参数。比如我自己写了一个计算器,入口位于bin\calculate.dart,则在命令行可以通过如下命令运行:
d:\workspace\dart\calculate>pub run calculate
若有指定目录,可以加上指定目录,如
pub run example\calculate
若要运行依赖项中的脚本,前面加上包名即可,比如我运行项目中依赖的表达式解析包(exp_parse)的例子parse.dart,其位于exp_parse/bin/parse.dart:
pub run exp_parse:parse
说重点:你只能运行位于其它 Package bin 目录下的脚本,其它目录下的脚本则不可运行。
4、pub global
Pub 的 global 选项允许你在任意位置下从命令行运行 Dart 脚本。在 激活 Package 后,你可以 运行 该 Package bin 目录下的脚本。停用 Package 后你可以从全局可用的 Package 列表中将其移除。该命令的用处比较多,下面详细介绍。
激活脚本(activate)
在使用全局命令之前,首先需要用activate参数激活package。该 Package 可以是在 pub.dev 网站、Git 仓库或者你当前的电脑上。一旦你激活了 Package,就可以运行位于 该Package bin 目录下的脚本。
激活 pub.dev 网站上的 Package
激活 pub.dev 网站上的一个 Package。例如激活markdown包:
2.2.5 核心库
1、dart:core
每一个 Dart 程序都可能会使用到的内置类型、集合以及其它的一些核心功能。
2、dart:async, package:async
支持通过使用 Future 和 Stream 这样的类实现异步编程。
package:async 提供了更多围绕 Future 和 Stream 构建的实用工具
3、dart:collection, package:collection
提供 dart:core 库中不支持的额外的集合实用工具类。
package:collection 则提供了更进一步的、用于处理和使用集合的函数和实现
4、dart:convert, package:convert
用于提供转换不同数据的编码器和解码器,包括 JSON 和 UTF-8。
package:convert 则提供了更多编解码器。
5、dart:developer
Interaction with developer tools such as the debugger and inspector. Native JIT and dartdevc only
6、dart:developer
类似调试器和分析器这样的与开发者交互配合的工具。 仅支持 Native JIT 和 dartdevc
7、dart:math
包含算术相关函数和常量,还有随机数生成器。
8、dart:typed_data, package:typed_data
高效处理固定大小数据(例如无符号的 8 位整型)和 SIMD 数字类型的列表。
package:typed_data 提供了更进一步的类和方法用于处理结构化的数据。
3 反射
3.1 反射类
3.1.1 ClassMirror
获取反射类对象
ps: flutter中禁止开发者使用mirrors,无法进行反射
external ClassMirror reflectClass(Type key);
Type 类型, 即 class类的类名(var、dynamic这些关键字不记入)
abstract class Test{} ClassMirror classMirror = reflectClass(Test); //类名即是Type
3.1.2 反射类分析
1、ClassMirror
abstract class ClassMirror implements TypeMirror, ObjectMirror {
ClassMirror get superclass; //父类 , Object的父类为null
List<ClassMirror> get superinterfaces; //接口列表
bool get isAbstract; //是否抽象类
bool get isEnum; //是否枚举类 //只包含自己原本的方法(构造方法、setter/getter、普通方法、静态方法)、成员(普通成员、静态成员),不包含继承来的 //注意: 属性是VariableMirror,实现了setter/getter的属性为MethodMirror
Map<Symbol, DeclarationMirror> get declarations; //包含构造方法、setter/getter、普通方法,包含继承来的
Map<Symbol, MethodMirror> get instanceMembers; //静态方法及静态属性的setter/getter方法,包含继承来的 //与instanceMembers合在一起就是类中全部的方法
Map<Symbol, MethodMirror> get staticMembers; //如果S = A with B ,那么ClassMirror(S).mixin 为 ClassMirror(B),否则返回本身
ClassMirror get mixin; /** * 调用构造方法 * constructorName 构造方法名称(默认构造方法为空字符串,命名构造方法为其命名) * positionalArguments 参数列表 */
InstanceMirror newInstance(Symbol constructorName, List positionalArguments,
[Map<Symbol, dynamic> namedArguments]);
bool operator ==(other); //判断是否相等
bool isSubclassOf(ClassMirror other); //判断是不是other的子类 }
2、Symbol
abstract class Symbol {
static const Symbol unaryMinus = const Symbol("unary-");
//构造方法,也可使用#来创建
static const Symbol empty = const Symbol("");
//Symbol("A") == #A
const factory Symbol(String name) = internal.Symbol;
int get hashCode;
bool operator ==(other);
}
Symbol创建
class A { var a = 1;}
Symbol("A") == #A //方法、类、参数直接用名字
Symbol("A.t") == #A.t //命名构造方法也是直接用名字
Symbol("a=") == #a= //属性set方法(私有属性即为#_a=)
Symbol("a") == #a //属性get方法
Symbol("==") == #== //操作符直接使用符号
Symbol("") == Symbol.empty
3、示例:使用构造方法
import 'dart:mirrors';
class A{
int a;
A(this.a){
print(a);
}
}
main(){
ClassMirror classMirror = reflectClass(A);
classMirror.newInstance(Symbol.empty ,[1]); //调用构造方法,打印1
}
3.2 方法反射
3.2.1 MethodMirror
abstract class MethodMirror implements DeclarationMirror {
TypeMirror get returnType; //反射类型
String get source; //source code , 不可用返回null
List<ParameterMirror> get parameters; //参数列表
bool get isStatic; //是否静态方法
bool get isAbstract;//是否抽象方法
bool get isSynthetic;//Synthetic方法(即隐式的setter/getter或构造方法[只定义属性或无构造函数])
bool get isRegularMethod; //常规方法(即非setter/getter、构造方法)
bool get isOperator; //操作符方法
bool get isGetter; //get方法
bool get isSetter; //set方法
bool get isConstructor; //判断是否构造方法
Symbol get constructorName; //获得构造方法的名字,默认构造方法为空字符串
bool get isConstConstructor; //常量构造方法
bool get isGenerativeConstructor;
bool get isRedirectingConstructor;//重定向构造方法
bool get isFactoryConstructor; //工厂构造方法
bool operator ==(other);
}
3.2.2 DeclarationMirror
abstract class DeclarationMirror implements Mirror {
Symbol get simpleName; //简称
Symbol get qualifiedName; //全称,包含路径 //库 -> null //类、顶级函数或变量、typedef -> 库 //S with M -> M //类中的方法、变量 -> 类 //函数中的局部变量、函数 -> 函数
DeclarationMirror get owner; //所有者
bool get isPrivate; //私有
bool get isTopLevel; //顶级 //格式:dart:core/runtime/libobject_patch.dart:53
SourceLocation get location; //来源,标明方法、类、属性所在文件的位置
List<InstanceMirror> get metadata; //元数据列表
}
3.2.3 示例:元数据对象获取
import 'dart:mirrors';
@Todo('todo', 'work')
class A{
int a;
A(this.a){
print(a);
}
}
class Todo {
final String who;
final String what;
const Todo(this.who, this.what);
}
main(){
ClassMirror classMirror = reflectClass(A);
// 获取 class 上的元数据
classMirror.metadata.forEach((metadata) {
print(metadata.reflectee.who + ' ==> ' + metadata.reflectee.what);
});
}
3.3 对象反射
3.3.1 InstanceMirror
abstract class InstanceMirror implements ObjectMirror {
ClassMirror get type; //类型
bool get hasReflectee;
get reflectee;//反射一个实体对象(hasReflectee==false,抛异常)
bool operator ==(other);
}
在得到一个InstanceMirror对象后,即可调用InstanceMirror.reflectee获得想要反射的对象
3.3.2 示例:反射对象
import 'dart:mirrors';
class A{
int a;
A(this.a){
print(a);
}
}
main(){
ClassMirror classMirror = reflectClass(A);
var instance = classMirror.newInstance(Symbol.empty ,[1]); //调用构造方法,打印1
print(instance.reflectee.a);//instance.reflectee: A对象, 打印1
}
3.4 属性反射
3.4.1 ariableMirror 类属性或顶级属性
isStatic 静态
isFinal
isConst
3.4.2 type 返回类型反射TypeMirror
ParameterMirror 函数的参数
isOptional 可选位置参数([])
isNamed 可选命名参数({})
hasDefaultValue 是否有默认值
defaultValue 获取默认值对象反射
3.5 类型反射
3.5.1 TypeMirror 比如:List<int>
reflectedType 类型 List<int>
typeVariables 类型变量集合 , E( List中类型变量是E )
typeArguments 类型集合 , int
3.5.2 TypedefMirror
TypeVariableMirror 代指泛型的类型变量(如: T)
upperBound 上界 , T extends M ,返回 M
isStatic
TypedefMirror 类型别名
referent 返回函数类型反射FunctionTypeMirror
3.5.3 FunctionTypeMirror 函数类型反射
//typedef A<T> = int Function(T a, T b); abstract class FunctionTypeMirror implements ClassMirror {
TypeMirror get returnType; //返回类型反射
int List<ParameterMirror> get parameters; // 参数反射集合
MethodMirror get callMethod;
}
3.5.4 示例
import 'dart:mirrors';
typedef F<T> = int Function(T a);
class A<T>{
List<int> a;
F<int> a2;
}
main(List<String> args) {
getAnnotation();
}
void getAnnotation() {
ClassMirror classMirror = reflectClass(A);
classMirror.declarations.forEach((Symbol key, DeclarationMirror value) {
//获取属性a和a2的反射
if(value is VariableMirror){
//获取属性的类型
var type = value.type;
print(type.reflectedType);
//函数类型反射
if(type is FunctionTypeMirror){
print(type.returnType);
type.parameters.forEach((value)=>print(value));
return;
}
type.typeVariables.forEach((value)=>print(value));
type.typeArguments.forEach((value)=>print(value));
}
});
}
}
3.6 总体示例
import 'dart:mirrors';
@Todo('todo1', 'A')
class A{
@Todo('todo2', 'a')
int a;
A(this.a){
print(a);
}
@Todo('todo3', 'doA')
doA(@Todo('todo4', 'x')int x){}
}
class Todo {
final String who;
final String what;
const Todo(this.who, this.what);
}
main(List<String> args) {
getAnnotation();
}
void getAnnotation() {
ClassMirror classMirror = reflectClass(A);
// 1.获取 class 上的元数据
classMirror.metadata.forEach((metadata) {
//因为示例就一个元数据,后续就省略判断了
if (metadata.reflectee is Todo) {
print(metadata.reflectee.who + ' ==> ' + metadata.reflectee.what);
}
});
//declarations,获取A类所有的方法和属性(没有继承)
classMirror.declarations.forEach((Symbol key, DeclarationMirror value) {
//属性是VariableMirror
if(value is VariableMirror){
// 2.属性上的元数据
value.metadata.forEach((metadata) {
print(metadata.reflectee.who + ' ==> ' + metadata.reflectee.what);
});
}
if (value is MethodMirror) {
// 3.方法上的元数据
value.metadata.forEach((metadata) {
print(metadata.reflectee.who + ' ==> ' + metadata.reflectee.what);
});
// 方法里的参数列表
value.parameters.forEach((param) {
//4.方法里参数的元数据
param.metadata.forEach((metadata) {
print(metadata.reflectee.who + ' ==> ' + metadata.reflectee.what);
});
});
}
});
}
4 Dart语言之“内置类型”
4.1 变量类型的核心概念
4.1.1 从内存角度看变量类型
底层变量设计原理
4.1.2 java中的类型
4.2 Dart类型设计差异
4.2.1 说明
int 在java中占4个字节;在dart中,如果编译成javaScript 的时候占32bit(4个字节),默认是64bit,因此dart中int的长度应该是根据环境来确定的,此处可以看作了java 一致;
在Dart中,我们可以将你int当成java中的short、int、long来使用,如果我们写的dart声明的变量超过了4个字节,那么Dart会将其编译成类似java中的long,否则编译成java中的short或者int
java中int 是基本数据类型,Dart中int 是类,所以两者的字节占用大小是没有可比性的;
dart源码中int类型注释
Dart源码地址
https://github.com/dart-lang
4.2.2 总结
1.变量类型底层的原理是一个数组,用来表示提取数据的长度
2.java中的基本类型设计遵循与C的底层原理,进行数据管理
3.Dart中的变量类型采取的是动态分配的方案,因此实际对应长度对于Dart来讲没有固定概念