Dart概述
- Dart是静态类型强类型的语言,如JAVA C#(在编译时就已知变量类型的语言既是静态语言)
- Dart是面向对象的语言,如Java,Kotlin,
- Jit&Aot,开发期间是JIT策略,发布版本使用Aot策略
JIT:Just In Time ,即时编译,开发期间,更快编译,更快重载,这也是Flutter能热更新的原因所在,但是Jit也是有劣势的,它会在运行时把代码编译成机械码
AOT:Ahead Of Time,预先编译,在安装时就把代码编译成机械码,这样在执行时就会更快更流畅
Dart基本概念
Dart HelloWord
void main() {
//打印字符串
print("HelloWord");
}
复制代码
- // 表示注释
- main 顶级函数,应用的程序入口
- var 定义变量,可以不指定数据类型,编译器会自动推断
Dart重要概念
- 一切皆对象:所有变量引用的都是对象;数字,函数,null都为对象,都继承自Object类
- Dart声明变量类型可选:Dart可以进行类型推断,例如用var定义变量,dynamic可以声明不确定的变量类型
- Dart支持泛型:例如List strs,或Map<String,int> map等
- Dart支持顶级函数,支持属于类或者对象的函数,支持嵌套函数
- Dart支持顶级变量,支持属于类或者对象的变量
- 标识符下划线表示库内私有:例如_name<私有变量>,_getName()<私有函数>,和Java中的private差不多
- 标识符字母,数字,下划线,由字母或者_开头
- Dart表达式有值,语句没有值
- Dart可以显示警告和错误两个类型
Dart内置数据类型
数据类型
int类型
长度不超过64位,具体取值范围依赖于不同的平台。在DartVM 上其取值位于-2的63次方至2的63次方- 1之间。编译成JavaScript的Dart使用JavaScript数字,其允许的取值范围在-2的53次方至2的53次方-1之间
double类型
64位的双精度浮点数字
num类型
数字类型的父类,可接受int double等所有数字类型
- num :数字类型的父类,可接受int double等所有数字类型
- int :是num类型的子类,只能接受int类型数据,例如:1,2等
- double: 是num类型子类,只能接受double类型数据,例如:1.0,2.2等
代码示例
void _numType() {
num num1 = -1.0;
num num2 = 2;
int num3 = 4;
double d1 = 1.0;
print("num1:$num1 num2:$num2 num3:$num3 d1:$d1");
print("${num1.abs()}");//求绝对值
print("${num1.toInt()}");//转为int
print("${num1.toDouble()}");//转为double
}
复制代码
打印结果
I/flutter ( 3368): num1:-1.0 num2:2 num3:4 d1:1.0
I/flutter ( 3368): 1.0
I/flutter ( 3368): -1
I/flutter ( 3368): -1.0
复制代码
字符串(String)
- Dart字符串是UTF-16编码的字符序列。可以使用单引号或者双引号来创建字符串
- 可以使用+运算符将两个字符串连接为一个,也可以将多个字段串挨着放在一-起
- 使用三个单引号或者三个双引号创建多行字符串
- 字符串前加上r作为前缀创建“raw” 字符串(不会被做任何处理)
示例代码
void _stringType() {
String str1 = "字符串双引号", str2 = '字符串单引号';
String str3 = "$str1$str2"; //字符串拼接,通过$
print(str3);
String str4 = str1 + str2; //字符串拼接,通过+
print(str4);
String str5 = str1.substring(0, 3); //截取前0-3的字符,包头不包尾
print(str5);
int index = str1.indexOf("引号"); //某些字符串位置
print(index);
bool isStart = str1.startsWith("哈哈"); //是否以某字符串开始
print(isStart);
}
复制代码
日志打印
I/flutter ( 3368): 字符串双引号字符串单引号
I/flutter ( 3368): 字符串双引号字符串单引号
I/flutter ( 3368): 字符串
I/flutter ( 3368): 4
I/flutter ( 3368): false
复制代码
bool类型
Dart中bool是是强bool类型检查,只有是bool类型的值,才能被赋值为true或者false;
- bool关键字表示布尔类型,布尔类型只有两个对象true和false,编译时常量
- Dart的类型安全不允许使用类似if(nonbooleanValue)的代码去检查布尔值
示例
void _boolType() {
bool success = true;
bool failed = false;
print("${success || failed}"); //或运算
print("${success && failed}"); //与运算
}
复制代码
结果
I/flutter ( 3368): true
I/flutter ( 3368): false
复制代码
List集合
- Dart中数组由List对象表示
- 下标从0开始
- List list = List();//固定长度为数组,无参为可变长度
Dart在2.3引入了扩展操作符(...)和null-aware扩展操作符(...?),它们提供了一种将多个元素插入集合的简洁方法
void _listType() {
List list1 = [1, 3, 5, "Hello", "Word"]; //初始化添加元素
print(list1);
List<num> list2 = [-1, 0, 1, 2.0]; //只能添加num类型的数据
print(list2);
List list3 = []; //添加元素形式
list3.add("你好");
list3.addAll(list1);
print(list3);
List list4 = List.generate(5, (index) => index * 5); //List生成函数生成
print(list4);
var list5=["拉阿拉",...list1];//使用...方式将list全部添加到list中
var list6=["拉阿拉",...?list1];//如果list可为空使用该方式
var list7 = [1, if (2 < 4) 3];//collection if方式
var list8 = [1, for (var i in list7) i];//collection for方式构造
//普通for遍历
for (int index = 0; index < list4.length; index++) {
print(list4[index]);
}
//in 形式遍历
for (var value in list4) {
print(value);
}
//forEach形式遍历
list4.forEach((element) {
print(element);
});
//元素值==0的位置,List必须是确定的类型的才行
list2.indexWhere((element) => element == 0);
}
复制代码
//结果
I/flutter ( 3368): [1, 3, 5, Hello, Word]
I/flutter ( 3368): [-1, 0, 1, 2.0]
I/flutter ( 3368): [你好, 1, 3, 5, Hello, Word]
I/flutter ( 3368): [0, 5, 10, 15, 20]
I/flutter ( 3368): 0
I/flutter ( 3368): 5
I/flutter ( 3368): 10
I/flutter ( 3368): 15
I/flutter ( 3368): 20
I/flutter ( 3368): 0
I/flutter ( 3368): 5
I/flutter ( 3368): 10
I/flutter ( 3368): 15
I/flutter ( 3368): 20
I/flutter ( 3368): 0
I/flutter ( 3368): 5
I/flutter ( 3368): 10
I/flutter ( 3368): 15
I/flutter ( 3368): 20
复制代码
Sets
- Dart中使用Set来表示无序且元素唯一的集合
- 支持Set字面量以及Set类型两种形式的Set
- Set字面量是在Dart2.2才加入的
示例代码
void _setType() {
var set1 = {"a", "b"};
Set<int> set2 = {}; //空set必须指定泛型类型,否则为map
Set<dynamic> set3 = {}; //不想指定类型则设置为dynamic类型
Set<int> set4 = {};
set4.add(1);
set4.add(2);
print(set1);
print(set4);
}
复制代码
执行结果
I/flutter (17944): {a, b}
I/flutter (17944): {1, 2}
复制代码
Maps
- Dart中Map通过Map字面量和Map类型来实现
- 每个键只能出现一次但是值可能重复出现多次 代码示例
void _mapType() {
var map1 = {1: "a", 2: "b"};
var value = map1[2]; //获取key为2的value
print(map1);
Map map2 = Map();
map2["key"] = "value"; //设置key,value
//forEach遍历
map2.forEach((key, value) {
print("$key|$value");
});
//迭代器遍历
var it = map2.keys.iterator;
if (it.moveNext()) {
var key = it.current;
var value = map2[key];
print("$key|$value");
}
//entries遍历
map2.entries.forEach((element) {
var key = element.key;
var value = element.value;
});
}
复制代码
结果
I/flutter (17944): {1: a, 2: b}
I/flutter (17944): key|value
I/flutter (17944): key|value
复制代码
Runes
- Dart使用Runes来标识UTF-32编码的字符串
- String类中codeUnitAt和codeUnit属性返回16位代码单元。runes 属性可以获取字符串的Runes
Symbols
- Symbol表示Dart中声明的操作符或者标识符,该类型的对象几乎不会被使用到
- 可以使用在标识符前加#前缀来获取Symbol
- Symbol字面量是编译时常量
Runes和Symbols使用场景比较小,了解即可
Dart方法
Dart是一种真正面向对象的语言,所以函数也是对象并且类型为Function,这意味着函数可以被赋值给变量或者作为其它函数的参数。你也可以像调用函数一样 调用Dart类的实例
参数
- 函数可以有两种形式的参数:必要参数和可选参数
- 必要参数定义在参数列表的前面,
- 可选参数则定义在必要参数的后面
可选参数
- 可选参数分为:命名参数和位置参数
- 可在参数列表中任选上述两种类型的一种,但是不可混用
命名参数
- 使用参数名:参数值的形式来指定命名参数
- 用大括号来指定命名参数
- 可以提供默认值
- @required注解来标识一个命名参数是必须的参数
位置参数
- 使用[]将系列参 数包裹起来作为位 置参数
- 可以用=为函数的位置参数定义默认值,默认值必须为编译时常量,没有指定默认值的情况下默认值为null 代码示例
void _methodType() {
print(addNum(1, 2));
print(addNum1(1, 2, c: 3));
print(addNum2(1, 2, 3));
}
int addNum(int a, int b) {
return a + b;
}
//命名参数,其中c是必传参数,d是可选命名参数
int addNum1(int a, int b, {@required int c, int d = 0}) {
return a + b + c + d;
}
//位置参数,其中??是个表达是 如果c=null则为0
int addNum2(int a, int b, [int c, int d = 0]) {
return a + b + (c ?? 0) + d;
}
复制代码
main()函数
- 每个Dart程序都必须有一个main()顶级函数作为其程序的入口,main()函数返回值为void并且有一个List 类型的可选参数
函数作为一级对象
- 可以将函数作为参数参数传递给另一个函数
- 可以将函数赋值给一个变量
匿名函数
- 没有命名的函数就是匿名函数
- 也可称之为lambda表达式或者Closure闭包
形式为:
([参数类型],[参数名称]){
函数体
}
示例
var e = (int ele) {//匿名函数
print(ele);
};
复制代码
胖箭头语法:如果函数体内只有一行语句,那就可以使用=>方式来书写
var e = (int ele) {//匿名函数
print(ele);
};
var f = (int ele) => {print(ele)};//匿名函数,胖箭头表达式
复制代码
函数示例
void _methodType2() {
var list = [1, 2, 3];
//forEach需要一个函数类型的参数
// 类型为参数为一个int,返回值为void的类型:Void Function(int) f
list.forEach(printIt);//把函数作为变量值传递
var d = printIt;//把函数赋值给变量
list.forEach(d);
var e = (int ele) {//匿名函数
print(ele);
};
var f = (int ele) => {print(ele)};//匿名函数,胖箭头表达式
list.forEach(e);
list.forEach(f);
addIt(1, printIt);//函数作为参数
addIt(2, e);
addIt(3, f);
}
void printIt(int i) {
print(i);
}
void addIt(int a, void func(int b)) {
func(a);
}
复制代码
词法作用域
- 变量的作用域在写代码的时候就确定了
- 大括号内定义的变量只能在大括号内访问
- 跟Java中局部变量,静态变量等差不多
词法闭包
- 闭包即一个函数对象,即使函数对象的调用在它原始作用域之外,依然能够访问在它词法作用域内的变量 示例
void _closureType() {
//adValue 返回一个函数,该函数其中一个值已经被赋值
var func1 = addValue(1);
//实际上func1已经等同于func2
var func2 = (int b) => 1 + b;
print(func1(2));
print(func2(2));
}
//adValue返回的是函数类型
Function addValue(int a) {
return (int b) => a + b;
}
复制代码
返回值
- 所有函数都有返回值
- 没有显示返回语句的函数最后一行默认执行return null 示例
void _returnType() {
//定义一个函数类型变量
var a = () {};
//比较null和a函数调用结果
bool b = null == a();
//比较null=函数引用
bool c = null == a;
print(b);
print(c);
}
复制代码
Dart运算符
运算符列表
Dart语言中运算符优先级,是上图中,从上到下,从左到右
算数运算符
运算符 | 描述 |
---|---|
+ | 加 |
- | 减 |
-表达式 | 一元负,也可以反转(反转表达式符号) |
* | 乘 |
/ | 除 |
~/ | 除并取整 |
++var | var=var+1(表达式的值为var+1) |
var++ | var=var+1(表达式的值为var) |
--var | var=var-1(表达式的值为var-1) |
var-- | var=var-1(表达式的值为var) |
示例
void operatorsTest() {
var a = 3 / 2;
var b = 3 ~/ 2;
var c = 1 / 3;
print(a);
print(b);
print(c);
}
复制代码
结果
1.5
1
0.3333333333333333
复制代码
关系运算符
运算符 | 描述 |
---|---|
== | 相等 |
!= | 不等 |
大于 | |
< | 小于 |
>= | 大于等于 |
<= | 小于等于 |
类型判断运算符
运算符 | 描述 | java比对 |
---|---|---|
as | 类型转换(也用作指定类前缀) | java中强转 |
is | 如果对象是指定类型则返回true | instance of |
is! | 如果对象是指定类型则返回false | instance of 取反 |
赋值运算符
- 使用= 来赋值
- 使用 ??= 来为值为null的变量赋值
- 算数运算符与赋值运算符结合也成为赋值运算符
逻辑运算符
运算符 | 描述 |
---|---|
!<!表达式> | 对表达式结果取反 |
11 | 逻辑或 |
&& | 逻辑与 |
按位和移位运算符
运算符 | 描述 |
---|---|
& | 按位与 |
1 | 按位或 |
按位亦或(相同为0,不同为1) | |
~表达式 | 按位取反 |
<< | 左移 |
>> | 右移 |
以上运算符 仅适用于整数int类型
条件表达式
- 主要用来代替if else的
void condition() {
var b;//默认为null
var c = b == null ? 0 : 1;
var d = b ?? 1;
}
复制代码
级联运算符
- 级联运算符(..)可以在同一个对象连续调用该对象的参数或者方法
class A {
String b;
int c;
void e() {}
}
void testClass() {
var aa = A();
aa
..b = "GG"
..c = 2
..e();
}
复制代码
其他运算符
运算符 | 名字 | 描述 |
---|---|---|
() | 使用方法 | 代表调用一个方法 |
[] | 访问List | 访问List特定位置的元素 |
. | 访问成员 | 成员访问符 |
?. | 条件访问成员 | 与成员访问符类似,但是左右操作对象不能为null,如果为null则返回null |
示例
class B {
int c;
}
void testClass() {
B bb;
bb?.c = 1;
print(bb?.c);
bb = B();
bb?.c = 1;
print(bb.c);
}
复制代码
输出
null
1
复制代码
流程控制语句
if 语句
- 根据特定表达式是否为true来有条件的执行另一个语句
- if 语句有两种形式:带else和不带else的
for循环语句
- 标准for循环: for(Initializer;condition:expression){ statement }
- 如果要遍历的对象实现了Iterable接口,则可以使用forEach()方法
- List和Set等实现了Literable接口的类还支持for-in 形式遍历
while do 和do while
- while do 循环在执行前先判断条件是否满足
- do while 循环先执行循环代码再判断条件
switch 语句
- switch 语句提供了一种更方便的方法来实现深层次的嵌套的if -else逻辑
- 每个非空 case语句必须有一个break语句,同时还可以通过continue,throw,return来结束非空case语句
- 没有case语句匹配时,可以使用default语句匹配默认情况
- case 语句内可以有局部变量,仅当前语句内可见
break continue语句
- break 语句用于结束最近的while,do while,for或者switch语句,并将程序的执行权传递给紧接在被终止语句之后的语句
- continue语句导致最近的循环语句的当次迭代提前结束
assert语句,断言语句
- 形式为:assert(condition,message)
- 当condition执行为false,中断正常执行,并打印出message
- 断言只在检查模式下运行有效,release阶段不会执行
异常
- Dart代码能够Throw和Catch异常。异常是一 些代码末知的错误情况。 如果异常没有被捕获 ,则异常会被抛出,最终导致代码终止执行。
- Dart中所有异常为非检查异常。方法不一定声明他们所抛出的异常,并且你也不需要捕获任何异常。
- Dart提供了Exception和Error类型,以及-些子类型。 也可以实现自己的异常类型。Dart 可以抛出任何非null对象为异常,不仅仅是实现了Exception或者Error的对象。
void throwTest() {
try{
}on FormatException catch(e){//通过on来匹配相应的异常
}on Exception catch(e){
}catch(e){//catch匹配之前未处理的异常
}finally{
//如果没有捕获异常,执行finally后异常会被抛出
//如果有catch finally会在catch之后执行
}
//抛出异常
throw FormatException("不匹配");
//抛出任意类型异常
throw "不匹配";
}
复制代码
类
Dart是一个面向对象编程语言,同时支持基于mixin的继承机制。每个对象都是一 个类的实例,所有的类都继承于Object.基于Mixin的继承意味着每个类( Object除外)都只有一个超类, 一个类的代码可以在其他多个类继承中重复使用。
使用类的成员
- 对象的成员由函数和数据(即方法和实例变量)组成,使用(.)来访问对象的实例变量或方法。
- 使用?.代替.可以避免因为左边表达式为null而导致的问题
使用构造函数
- 可以使用构造函数来创建一个对象。 构造函数的命名方式可以为类名或类名.标识符的形式。
- 从Dart2.0开始,new 关键字是可选的
实例类的变量
- 所有未初始化的实例变量其值均为null。
- 所有实例变量均会隐式地声明一个Getter方法,非final 类型的实例变虽还会隐式地声明一个Setter方法。
- 如果你在声明一个实例变量的时候就将其初始化,那么该实例变量的值就会在对象实例创建的时候被设置,该过程会在构造函数以及它的初始化器列表执行前。
构造函数
- 声明一个与类名一样的函数即可声明一个构造函数
- 对于大对数的变成语言来说构造函数中为实例变量赋值的过程都是类似的,Dart提供了一种特殊的语法糖来简化该操作 例如
class Person {
int age;
String name;
// Person(int age, String name) {
// this.age = age;
// this.name = name;
// }
Person(this.age,this.name);
}
复制代码
默认构造函数
如果你没有声明构造函数,那么Dart会自动生成一个无参 数的构造函数并且该构造函数会调用其父类的无参数构造方法。 构造函数不被继承 子类不会继承父类的构造函数,如果子类没有声明构造函数,那么只会有一个默认无参数的构造函数。
命名式构造函数
- 可以为一个类声明多个命名式构造函数,来表达明确的意图
- 在子类中提供一个与父类构造函数名字一样的命名函数,则需要在子类中显式的声明
重定向构造函数
有时候类中的构造函数会调用类中其它的构造函数,该重定向构造函数没有函数体:只需在函数签名后使用(: )指定需要重定向到的其它构造函数即可。 示例
class Person {
int age;
String name;
Person(this.age, this.name);
//命名式构造函数
Person.origin() {
age = 0;
name = "";
}
//命名式构造函数
Person.initAge(int age) {
this.age = age;
}
//引用式构造函数
Person.initName(String name):this(0,name);
void testCon() {
Person p1 = Person(1, "hh");
Person p2 = Person.origin();
Person p3 = Person.initAge(2);
Person p4 = Person.initName("hh");
}
复制代码
常量构造函数
- 如果类生成的对象都是不会变的,那么可以在生成这些对象时就将其变为编译时常量。你可以在类的构造函数前加上const关键字并确保所有实例变量均为final 来实现该功能。
- 常量构造函数创建的实例并不总是常量
class NewPoint {
final int x, y;
const NewPoint(this.x, this.y);
}
void testPoint() {
var a = const NewPoint(1, 1);
var b = const NewPoint(1, 1);
var c = const NewPoint(1, 2);
assert(identical(a, b));//可执行过去
assert(identical(b, c));//程序中断
}
复制代码
工厂构造函数(重点)
- 使用factory关键字标识类的构造函数将会令该构造函数变为工厂构造函数,这将意味着使用该构造函数构造类的实例时并非总是会返回新的实例对象。
- 工厂构造函数中是无法访问this
main(List<String> args) {
var log1=Logger();
var log2=Logger();
print(log1==log2);
}
class Logger {
static Logger _cache;
factory Logger(){
if (_cache==null) {
_cache=Logger._internal();
}
return _cache;
}
Logger._internal();
}
复制代码
结果:true
命名工厂构造方法(重点)
- 命名工厂构造方法:factory 类名 . 方法名
- 它可以有返回值,而且不需要将类的final变量作为参数,是提供一种灵活获取对象的构造方式
- 命名构造方法则必须有final变量
初始化列表
- 在构造函数体执行之前初始化变量
- 初始化列表表达式=右边的语句不能使用this关键字
- 如果父类没有构造方法(无参构造方法),则需要在初始化列表中调用父类的构造方法
调用父类构造函数
- 默认情况下,子类的构造函数会调用父类的匿名无参数构造方法,并且该调用会在子类构造函数的函数体代码执行前,如果子类构造函数还有一个初始化列表,那么该初始化列表会在调用父类的该构造函数之前被执行 总的来说,这三者的调用顺序如下:
- 初始化列表
- 父类的无参数构造函数
- 当前类的构造函数
- 如果父类没有匿名无参数构造函数,那么子类必须调用父类的其中一个构造函数,为子类的构造函数指定一个父类的构造函数只需在构造函数体前使用 (:)指定。
class OldPoint {
int x, y;
OldPoint(this.x, this.y);
}
class NewPoint extends OldPoint {
int x, y;
NewPoint(this.x, this.y) : super(x, y);
}
复制代码
方法
实例方法
对象的实例方法可以访问实例变量和this
import 'dart:math';
class OldPoint {
int x, y;
OldPoint(this.x, this.y);
num getDistance(OldPoint point) {
int x1 = (point.x - x).abs();
int y1 = (point.y - y).abs();
return sqrt(x1 * x1 + y1 * y1);
}
}
复制代码
Getter和Setter
Getter和Setter 是一对用来读写对象属性的特殊方法,实例对象的每一个属性都有一个隐式的Getter方法,如果为非final属性的话还会有一个Setter 方法,你可以使用get和set关键字为额外的属性添加Getter和Setter方法。
示例
class Range {
num left, top, width, height;
Range(this.left, this.top, this.width, this.height);
num get right => left + width;
set right(num value) => left = value - width;
}
void testRange() {
var ran = Range(1, 1, 3, 3);
print(ran.right);
ran.right = 5;
print(ran.left);
}
复制代码
结果
4
2
复制代码
抽象方法
- 定义一个接口方法而不去做具体的实现让实现它的类去实现该方法,抽象方法只能存在于抽象类中.
一个类定义了抽象方法,那么该类必须是抽象类
抽象类
- 使用关键字abstract标识类可以让该类成为抽象类,抽象类将无法被实例化。抽象类常用于声明接口方法、有时也会有具体的方法实现。
- 抽象类常常会包含抽象方法,但是抽象类也不一定有抽象方法
- 非抽象类的子类继承抽象类必须实现父类的抽象方法
abstract class People{
void eatTh();
}
class Man extends People{
@override
void eatTh() {
// TODO: implement eatTh
}
}
复制代码
隐式接口
每一个类都隐式地定义了一个接口并实现了该接口, 这个接口包含所有这个类的实例成员以及这个类所实现的其它接口。一个类可以通过关键字implements来实现一个或多个接口 并实现每个接口定义的API。
class NewPeople {
final String _name;
NewPeople(this._name);
String greet(String who) => "你好,$who,我是$_name";
}
class Man implements NewPeople {
@override
String get _name => "";
@override
String greet(String who) => "你好$who,你知道我是谁吗";
}
String greetBob(NewPeople person) => person.greet("Bob");
print(greetBob(NewPeople("阿兰")));
print(greetBob(Man()));
复制代码
结果
你好,Bob,我是阿兰
你好Bob,你知道我是谁吗
复制代码
扩展类
- 使用extends 关键字来创建一个子类,并可使用super关键字引用一个父类
- 子类可以重写父类的实例方法,Getter和Setter方法
重写运算符
!= 运算符不可被重写,表达式e1!=e2 仅仅是!(e1==e2)的语法糖
- 如果调用了对象上不存在的方法或实例变量将会触发noSuchMethod方法,你可以重写noSuchMethod方法来追踪和记录这一操作。
枚举
- 使用enum定义枚举类型
- 每一个枚举的值都有一个名为index成员变量的getter方法,该方法返回以0为基准索引的位置值
- 使用枚举类的values方法获取一个包含所有枚举值的列表
- 在switch 语句重视使用枚举,需要注意的是必须处理枚举值的每一种情况
Mixin
- 定义一个类继承自Object并且不为该类定义构造函数,这个类就是Mixin类,除非你想让该类与普通的类一样可以被正常地使用,否则可以使用关键字mixin替代class让其成为一个单纯的Mixin类。
- 使用with关键字并在其后跟上Mixin类的名字来使用Mixin模式。多个的话以 , 隔开
mixin其实就是Java中带默认实现的接口 具体的可以参考以下文章
类变量和方法
- 静态变量(即类变量)常用于声明类范围内所属的状态变量和常量。静态变量在其首次被使用的时候才被初始化。
- 静态方法(即类方法)不能被一个类的实例访问,同样地,静态方法内也不可以使用this.
泛型
为什么使用泛型
- 正确指定泛型类型可以生成更好的代码
- 使用泛型可以减少重复代码量
- 使用集合字面量
List、Set 以及Map字面量也可以是参数化的。定义参数化的List只需在中括号前添加 ;定义参数化的Map只需要在大括号前添加<keyType, valueType> :
- 构造方法时也可以使用泛型,在类名后用(<...>)讲一个或者多个类型包裹
- Dart的泛型类型是固化的,这意味着即便在运行时也会保持类型信息
java中的泛型是类型擦除的,你可以判断对象是否为List,但是不可以判断List对象是否为List< String >
- 使用泛型的时候可以通过extentds关键字限制泛型类型范围.
泛型方法
方法 methodA< T >的泛型 T可以在如下位置使用:
- 函数的返回值类型(T)
- 参数的类型<List< T >>
- 局部变量的类型 (T temp)
Dart编程技巧
- 安全调用
对于不确定是否为空的对象可以通过?.的方式,来访问它的属性和方法以防止空异常
- 设置默认值
善于使用 ?? 形式给可能为空的情况设置默认值
- 简化判断
对于需要多个判断的,可以用contains一起判断
if (list[0] == null || list[0] == '' || list[0] == 0) {
print('list[0] is empty');
}
if ([null, '', 0].contains(list[0])) {
print('list[0] is empty');
}
复制代码