这个转自我自己的有道云 想看图片去那里
文档:Day2_24 Dart语法.md
链接:http://note.youdao.com/noteshare?id=6c7875ef0d88c3e13440ccbec7d1e1b9&sub=55C6064F712D4B239B8D3FB7EDE5C5E0
Dart语法
认识Dart
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V81E4fVK-1582943518348)(452F75BF87FB4F5D951E4B66EBAAF821)]
安装dart
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nPxvmPRf-1582943518349)(21A1E482D3754066ACD4C621151CCB95)]
其实可以
基础语法
每个语句以后必须打 分号
hello_dart.dart
print是重要的打印语句
main(List<String> args ){
print("hello world");
}
args 是用终端调用的时候可以传的参数
- 声明变量
注意变量的声明在dart里面有两种 一种是 明确的声明 另外一种是 类型推导
- 明确的声明
这点和java非常的相似
String name = "why";
print(name);
- 类型推导(var/final/const)
类型推导虽然没有指定变量的类型, 但是它是有类型的
这样编译器就会报错
main(List<String> args) {
// 2. 类型推导
var age = 20;
age = "abc";
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qn4yZ4O5-1582943518350)(7E9BBAE3432E47E8B474E4BEEDB50437)]
2.1 但是如果确实希望像js一样使用我们可以使用dynamic (同时再涉及一下Object)
dynamic 本来的意思就是动态的
dynamic age = 20;
age = "abc";
print(age);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R7vGvKli-1582943518350)(0BF83693CACD4613B3BDC00FAC2E9876)]
object 他是所有的对象的父类
但是像下面这样其实和dynameic 不一样 它原理其实因该更靠近 父类引用指向字类对象
Object name = "why";
// 这个toString 方法就是Object对象里面的
print(name.toString());
上面这样是可以的
但是下面这样是不行的
Object name1 = "why";
// 父类引用指向字类对象
// 所以父类的引用肯定 肯定没有办法指向字类的方法
print(name1.substring(1));
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JoSEOXE8-1582943518351)(3D220CA8BB7D4833A3E7A066E97D3426)]
编译器说Object中没有定义这个substring的方法
但是dynamic 就可以实现这个功能
// 但是dynameic 就可以实现这个功能
dynamic name2 = "why";
print(name2.substring(1));
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XX9Ob1DP-1582943518351)(796711B788F142BEA091CE793A26FEBD)]
2.2 声明常量 final/const
声明一个常量只能在声明的时候赋值
main(List<String> args) {
// 2.2 声明常量
final height = 1.88;
// height = 2.00; 不能改变常量的值
// final weight; 这样初始化是不行的
// weight = 20;
}
2.3 const 声明常量
同样是声明一个常量在声明的时候赋值
main(List<String> args) {
// 2.3 const 声明常量
const address = "广州市";
// address = "test"; 同样不能改变它的值
}
2.4 const 和 final的区别
既然const 和 final的作用一样但是他们也是有区别的
- const 必须赋值 常量值(编译期间需要有一个确定的值)
- final 可以通过计算/函数获取一个值(运行期间来确定一个值)
// 2.4 final 和 const 的区别
// const date1 = DateTime.now(); const 只能被赋值为常量的值 返回值是不行的
final date1 = DateTime.now();
print(date1);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GE1syxEr-1582943518352)(97649220AB8C4EE3A3401C42299C7714)]
使用中还是final用的多一点, 因为它可以运行时被赋值
但是const 还是有用 它在创建这种重复使用的类的时候就可以节省内存(大概)
// 2.5 用const 修饰构造函数
// 这里是在生成对象 所以
// dart 2.0 以后可以省略 new 创建对象时
var p1 = Person1("why");
var p2 = Person1("why");
// 这个函数是用来判断两个对象是否相等
print("identical(p1, p2)");
print(identical(p1, p2));
// 使用const 修饰的变量 如果传入的参数是相同的 他们的对象是相同的
// 这个严格意义上不是单例 主要是为了节省内存
const p3 = Person2("why");
const p4 = Person2("why");
print("identical(p3, p4)");
print(identical(p3, p4));
class Person2 {
final String name ;
const Person2(this.name);
// 这里其实是初始化列表的使用方式
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MOBDE2YW-1582943518352)(71FA5F94652F4951934C056FB5B2386E)]
- dart 没有非零即真和非空即真
如果在判断的地方有空或者是0 编译器就会报错
main(List<String> args) {
var flag = 0;
if(flag) {
print("这段话没有办法打印");
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x689sSRQ-1582943518353)(828603405E9644BE97649737674A6EAA)]
- 4.数据类型
4.1 数字类型
对于数值来说我们也不用关心他是否有符号, 以及数据的宽度和精度等问题.只要记住整数用int , 浮点数用double就可以了
但是要注意一下, Dart中的 int和 double 可表示的范围并不是固定的, 它取决于运行Dart的平台
main(List<String> args) {
// 1. int 类型
int age = 18;
int hexAge = 0x12;
print(age);
print(hexAge);
// 2. 浮点类型double
double height = 1.88;
print(height);
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DAsYSTP4-1582943518353)(D170B1D9447F4747BC366C8A97C31D75)]
4.1.2 字符串和数值之间的转化
// 1. 字符串转化成数值
var one = int.parse("123");
var two = double.parse("20.22");
print("${one}, ${one.runtimeType}");
print("${two}, ${two.runtimeType}");
// 2. 数值转化成字符串
var num1 = 123;
var num2 = 123.456;
var num1str = num1.toString();
var num2str = num2.toString();
var num2strD = num2.toStringAsFixed(2); // 保留两位小数
print('${num1str}, ${num1str.runtimeType}');
print('${num2str}, ${num2str.runtimeType}');
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NXApG0bN-1582943518353)(AFF91050B9974901B587D073537BDEC8)]
4.2 布尔类型
var isFlag = true;
print("$isFlag ${isFlag.runtimeType}");
if (isFlag ) {
print("这段代码可以执行");
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KLKSaFx4-1582943518354)(91C78133B07E4D71BFC1E051791AE86D)]
注意dart里面没有非零即真 也 没有非空即真
如果用了会报错
var flag = "abc";
if(flag) {
print("这段代码不能执行");
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FTUce5C5-1582943518354)(2819973C38C8463E87E9FC8C65D2C5F7)]
4.3 字符串类型
Dart 字符串是UTF-16 编码的序列, 可以使用单引号和双引号
var s1 = "Hello world";
var s2 = 'Hello world';
var s3 ="Hello\" world";
var s4 = "Hello'Flutter";
print(s1);
print(s2);
print(s3);
print(s4);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GxvdG3rr-1582943518355)(E4ACA09339874AEB9C2811D4746D40AF)]
可以使用三个单引号或者双引号表示多行字符串
// 2. 表示多行字符串的方式
var message = '''
test
hahaha
嘿嘿嘿
''';
print(message);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WWHetxhA-1582943518355)(9D8ED65C63934FA29D7389FA9D208B5E)]
4.4 集合类型
所谓集合就是数组啊对象啊这种数据的容器, 但是一般特指具有特殊数据结构的容器
对于集合类型, Dart则内置了最常用的三种: List / set / Map
List
其中List可以这样来定义 看着像数组
// List的定义 列表List: []
// 1. 使用类型推导定义
var letters = ["a", "b", 123, 2.22, "d"];
print('$letters, ${letters.runtimeType}');
// 2. 明确指定类型
List<int> numbers = [1, 2, 3, 4];
print("$numbers, ${numbers.runtimeType}");
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uDvXyA1y-1582943518355)(CEF19E6AB7C8487CBA26239F4DB8A5C2)]
Set
其实set也是可以这样定义的
4.4.2.1 其实, 也就是把[]换成{}就好了
4.4.2.2 Set和List最大的不同就是: Set是无序的, 而且元素是不重复的
// Set的定义 集合Set: {}
// 1. 使用类型推导定义
var lettersSet = {"a", "b", "c", 123};
print("$lettersSet ${lettersSet.runtimeType}");
// 2. 明确指定类型
Set<int> numberSet = {1,2,3,4};
print('$numberSet ${numberSet.runtimeType}');
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1TFe3bXd-1582943518356)(ACEB020D0DE543A889380EBFA94D363B)]
同时因为集合是不重复的, 所以我们可以通过这个性质来给数组去重
// 因为集合是不重复的所以我们可以用这个方式来给数组去重
List<String> movies = ["大话西游", "樱 恋萌芽", "大话西游", "变形金刚"];
movies = Set<String>.from(movies).toList();
print(movies);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3rz7RPO8-1582943518356)(F97D531439814845978660E62D819EB5)]
Map
最后Map是我们常说的字典类型 hash表
// Map 的的定义
// 1. 使用类型推导定义
var infoMap1 = {"name": "why", 'age': 18};
print("$infoMap1 ${infoMap1.runtimeType}");
// 2. 明确指定类型
Map<String, Object> infoMap2 = {"height": 1.88, "address": "北京市"};
print("$infoMap2 ${infoMap2.runtimeType}");
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sye7JZDR-1582943518357)(A19497603296436FA100CE91ED5B6BFE)]
4.4.2 集合的常见操作
了解这三个集合的定义方式以后,我们来看看最基础的公共操作
第一类, 是所有集合都支持的获取长度的属性length
print(numbers.length);
print(lettersSet.length);
print(infoMap1.length);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gWHbo1y3-1582943518357)(8DACC583CB6C4B0D946C07FE3849C31C)]
第二类 是添加 删除 包含
并且, 对于List来说, 忧郁元素是有序的, 它还提供了一个删除指定索引位置上元素的方法
// 添加/删除/包含
numbers.add(5);
numberSet.add(5);
print("$numbers $numberSet");
numbers.remove(1);
numberSet.remove(1);
print("$numbers $numberSet");
print(numbers.contains(2));
print(numberSet.contains(2));
// List根据index删除元素
numbers.removeAt(3);
print("$numbers");
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ketxKnN3-1582943518357)(0E961932C2A34E3CBBF2399D10C4F290)]
第三类 是Map操作
由于它有key和value, 因此无论是读取值, 还是操作, 都要明确是基于key的, 还是基于value的, 或者是基于key/value 对(键值对)的
// Map操作
// 1. 根据key获取value
print(infoMap1["name"]); // why
// 2. 获取所有的entries
print("${infoMap1.entries} ${infoMap1.entries.runtimeType}");
// 3. 获取所有的keys
print("${infoMap1.keys} ${infoMap1.keys.runtimeType}");
// 4. 获取所欲的values
print("${infoMap1.values} ${infoMap1.values.runtimeType}");
// 5. 判断是否包含某个key 或者values
print("${infoMap1.containsKey("age")} ${infoMap1.containsValue(18)}");
// 6. 根据key删除元素
infoMap1.remove("age");
print("$infoMap1");
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uWRqP7lU-1582943518358)(83FF46AD693148E6AD41D691646A60F0)]
- 5.函数的基本定义
Dart 是一种真正的面向对象的语言, 所以即使是函数也是对象, 也有类型, 类型就是Function
函数的定义方式:
返回值类型 函数的名称(参数列表) {
函数体
return 返回值
}
main(List<String> args) {
}
int sum(int num1, int num2) {
return num1 + num2;
}
同时我们是可以省略调返回值的
5.2 函数的参数问题
函数的参数分为两类: 必须参数和可选参数
5.2.1 可选参数
可选参数分为 命名可选参数 和 位置可选参数
定义方式
命名可选参数: {param1, param2}
位置可选参数: [param1, param2]
位置可选参数
位置可选参数 位置和参数类型必须对应
main(List<String> args) {
// 如果sayHello(); 就会报错
sayHello("输出的字符");
sayHello1("输出的字符");
sayHello1("输出的字符", 18);
}
// 这个地方参数是必须传的
// 不传编译器都会报错
void sayHello(String name) {
print(name);
}
// 位置可选参数
// 意思是传是根据位置来的如果对应位置没有就不传
// 如果有的话就可以传 但是类型必须和位置对应 如果排在第二位是需要int类型 而传过来的第二位是字符串就会报错
void sayHello1(String name, [int age]) {
print("$name $age");
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aDves764-1582943518358)(CE2A2F122A9C4870A5DED7F1664AF311)]
命名可选参数
main(List<String> args) {
sayHello2("输出的字符串", age: 18, height: 1.88);
sayHello2("输出的字符串", height: 1.88);
sayHello2("输出的字符串", age: 18);
}
// 位置可选参数
// 意思是传是根据位置来的如果对应位置没有就不传
// 如果有的话就可以传 但是类型必须和位置对应 如果排在第二位是需要int类型 而传过来的第二位是字符串就会报错
void sayHello1(String name, [int age]) {
print("$name $age");
}
void sayHello2(String name, {int age, double height}) {
print("name = $name, age = $age, height = $height");
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u9TPtdUX-1582943518359)(A9F8BBA5DFD84BD0BA192ABF6940912E)]
5.2.2 默认参数
main(List<String> args) {
sayHello3("test");
sayHello4("test");
}
void sayHello3(String name , {int age = 18, double height = 1.88}) {
print("$name $age $height");
}
void sayHello4(String name, [int age = 18, double height = 1.88]) {
print("$name $age $height");
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eOwmRzLJ-1582943518359)(02144215A10748AFBE6372E1225D2C9D)]
- 函数是一等公民
在面向对象的语言中 类是一等公民
类(对象) 是第一等民 所以在各种以类作为第一公民(java/OC)的语言中 我们可以将类(对象)作为参数来传递
可以作为方法的参数, 也可以作为方法的返回值
但是在很多语言中, 函数并不能做为一等公民来使用, 这种限制让变成不够灵活, 所以现代编程语言基本都支持函数作为一等公民来使用, Dart也支持
这意味这两点
- 你可以将函数赋值给一个变量
- 也可以将函数作为一个函数的返回值来使用
具体来说就是
- 可以直接找到对应的函数名传进去
- 可以传任意函数 代参数的 不带参数
- 可以使用可以在去回调的函数里面写出回调函数的参数类型
- typedef 使用函数签名 说白了就是
- 可以将函数作为返回值
main(List<String> args) {
// 1. 可以直接找到对应的函数名传进去
test(bar);
// 2. 可以传任意函数 代参数的 不带参数
test(() {
print("使用匿名函数");
});
// 箭头函数: 条件函数只有一行代码的时候才能使用
test(() => print("使用匿名函数"));
// 可以带参数 可以不带参数
test(([int abc = 20]) {
print("test");
});
// 3. 可以使用可以在去回调的函数里面写出回调函数的参数类型
// 但是同时这种方式存在一个问题
// 就是里面的函数作为参数变量太长不好看
testWithFunctionParamters((age, height) {
print("$age $height");
});
// 4. typedef 使用函数签名 说白了就是
// 将调用回调函数的 参数 就是回调函数的类型
// 作为一个标签保存起来 后面再用作 参数传进去
test2((num1, num2) {
return num1 + num2;
});
// 5. 可以将函数作为返回值
print(demo()(20, 90));
}
void test(Function foo) {
foo();
}
void bar() {
print("我是bar函数");
}
void testWithFunctionParamters(void foo(int age, double height)) {
foo(20, 1.88);
}
typedef Cal = int Function(int num1, int num2);
void test2(Cal calc) {
print(calc(20, 30));
}
Cal demo() {
return (num1, num2) {
return num1 + num2;
};
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KxercB70-1582943518359)(E1B6A1EA4A5F463FAE29854DE84BCC51)]
匿名函数
() {
print("这个是一个匿名函数");
}
词法作用域
dart中有块级作用域
当内部作用域找不到时就会到外面去找, 这个和java很像
var name = "gobal";
main(List<String> args) {
// (( ) {
// print("这是一个匿名函数");
// } )();
// }
// var name = "main";
void foo( ) {
// var foo = "foo";
print(name);
}
foo();
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CyCQ2g3V-1582943518359)(34A954B46DC24EBA8E31EB33029658F7)]
词法闭包
同样它有回调就有闭包
闭包可以访问其词法范围内的变量, 即使函数在其他的地方被使用, 也可以正常访问
makeAdder(num addBy) {
return (num i ) {
return i + addBy;
};
}
var addr2 = makeAdder(2);
print(addr2(10));
print(addr2(22));
var addr3 = makeAdder(3);
print(addr3(10));
print(addr3(22));
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hi1nd1Kz-1582943518360)(58FD0C0B0A204259A52DA343C33A54D6)]
- 运算符
我们就说一些其他语言不常见的就可以了
??= 赋值操作
- 当变量为null时, 使用后面的内容进行赋值
- 当变量有值的时候, 使用自己原来的值
main(List<String> args) {
var name1 = "coderwhy";
name1 ??= "test";
print(name1);
var name2 = null;
name2 ??= "test";
print(name2);
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1wo0apuj-1582943518360)(4D1CDB4CD72D4E8686B921193113204A)]
条件运算符 ??
- 如果expr1是null, 则返回expr2的结果
- 如果expr1不是null, 则直接使用expr1的结果
这个运算符吗在一些情况下和js的 || 作用相同
var value1 = null;
var value3 = "test";
var value2 = "coder";
print(value1 ?? value2);
print(value3 ?? value2);
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UHy3TI3K-1582943518360)(C023A5F83FA246CF9FC8BE8B7E1C9F9A)]
级联语法
为链式调用做准备
main(List<String> args) {
var p1 = Person("coder", 19)
..eat()
..drink();
}
class Person {
String name;
int age;
Person(this.name, this.age);
eat() {
print("吃饭");
}
drink() {
print("喝水");
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bx0O9My3-1582943518361)(AB0E91542F804C22BA14F6A33946BA3C)]
- 流程控制
和大多数语言用法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J9Fy8bqq-1582943518361)(2B05A756390044349FC229B4EBD381AC)]
同样是有迭代器的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dI3pmR51-1582943518361)(6A69B72D18074E34AFAF10AEA1DA2A86)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zdhK5ZQh-1582943518361)(63F0A5E057604A9B8BE92513753999EE)]
- 类
Dart 是一个面向对象的语言, 面向对象中的概念就是类, 类产生了对象.
这一节, 我们具体来学习类和对象, 但是Dart 对类进行了很多创新, 所以我们就要讲这部分的内容
3.2 类的构造方法
我们通过类创建一个对象的时候我们会使用构造方法
而Dart中
- 当类中没有明确指定的构造方法的时, 将默认拥有一个无参的构造方法
- 前面Person中我们就是调用这个构造方法
- 当我们自己定义了构造方法的时候原有的构造方法就会消失(同时注意Dart中式没有重载的)
- 我们可以重写父类的toString方法
main(List<String> args) {
print(new Person("test", 12));
}
class Person {
String name;
int age;
Person(String name, int age ) {
this.name = name;
this.age = age;
}
@override
String toString() {
// TODO: implement toString
return "name = $name, age = $age";
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gqi1abZ3-1582943518362)(40BE7C11203D4530AFE64B29E5698531)]
另外构造函数可以被简化
上面的构造方法可以优化成下面的
main(List<String> args) {
print(Person1("test", 13));
}
class Person1 {
String name;
int age;
Person1(this.name, this.age);
@override
String toString() {
// TODO: implement toString
return "name = $name, age = $age";
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UlfvMVti-1582943518362)(16F9F95984F646E8AF1B1CA1B5DFA914)]
3.3 命名构造方法
因为在Dart 因为不支持函数的重载, 我们没有办法创建相同名称的构造方法
我们这个时候就可以使用命名构造函数
main(List<String> args) {
print(new Person());
print(new Person.withArgments("test", 12));
}
class Person{
String name;
int age;
Person() {
name = "";
age = 0;
}
Person.withArgments(String name, int age) {
this.name = name;
this.age = age;
}
@override
String toString() {
// TODO: implement toString
return "name = $name, age = $age";
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rKgUydMW-1582943518362)(F4BC2BFF3E5644A8A52A0FD7425A63B2)]
我们也可以通过命名构造函数, 提供更加快捷的对象的创建方式
- 比如开发中, 我们需要经常将一个Map对象转化成对象, 可以提供如下的构造方法
main(List<String> args) {
print(new Person());
print(new Person.withArgments("test", 12));
print(new Person.withMapArgments({"name": "test2", "age": 123}));
}
class Person{
String name;
int age;
Person() {
name = "";
age = 0;
}
Person.withArgments(String name, int age) {
this.name = name;
this.age = age;
}
Person.withMapArgments(Map<String, Object> map) {
name = map["name"];
age =map["age"];
}
@override
String toString() {
// TODO: implement toString
return "name = $name, age = $age";
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qQhIWeBT-1582943518363)(52FB6A2C40D34F7DA94CE6D07232B76E)]
3.2.3 初始化列表
我们来重新定义一个类Point, 传入, 可以得到他们的距离distance
import 'dart:math';
main(List<String> args) {
var p1 = Point(2.0, 3.0);
print(p1);
}
class Point {
final num x;
final num y;
final num distance;
// 错误的写法
// 因为在执行下面的代码的时候对象的初始化已经完成了, 所以不能对它再进行赋值
// Point(this.x, this.y) {
// distance = sqrt( x * x + y * y);
// }
// 正确的写法
Point(this.x, this.y) : distance = sqrt(x * x + y * y);
@override
String toString() {
// TODO: implement toString
return "x = $x, y = $y, distance = $distance";
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2OYWCSlp-1582943518363)(6B1D671DC0924AB89BF44F77BFD3E8FD)]
我们可以通过初始化列表来初始化被 final 定义的属性
常量的构造方法更是需要, 初始化列表来为参数赋值
// 2.5 用const 修饰构造函数
// 这里是在生成对象 所以
// dart 2.0 以后可以省略 new 创建对象时
var p1 = Person1("why");
var p2 = Person1("why");
// 这个函数是用来判断两个对象是否相等
print("identical(p1, p2)");
print(identical(p1, p2));
// 使用const 修饰的变量 如果传入的参数是相同的 他们的对象是相同的
// 这个严格意义上不是单例 主要是为了节省内存
const p3 = Person2("why");
const p4 = Person2("why");
print("identical(p3, p4)");
print(identical(p3, p4));
class Person2 {
final String name ;
const Person2(this.name);
// 这里其实是初始化列表的使用方式
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fQN5tazy-1582943518363)(71FA5F94652F4951934C056FB5B2386E)]
3.2.4 在构造函数中调用另外的构造函数(重定向构造函数)
在某些情况下, 我们希望在一个构造方法中去调用另外一个构造方法, 这个时候我们可以使用重定向构造方法
main(List<String> args) {
print( Person.fromName("test") );
}
class Person {
String name;
int age;
Person(this.name, this.age);
Person.fromName(String name) : this(name, 0);
@override
String toString() {
// TODO: implement toString
return "name = $name, age = $age";
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1jVbCKaw-1582943518364)(27C3F4EC23C446FBB0A291B3EAE413E8)]
3.2.5 常量构造方法
有些时候, 在传入相同的值的时候, 我们希望返回相同的对象, 这个时候我们可以使用常量构造方法, 创建对象的时候, 即使传入相同的参数, 创建出来的也不是同一个对象
但是如果我们使用常量构造函数, 我们就能得到传入相同的参数, 得到相同的对象
但是严格意义上我们这个东西不算是单例, 所以我们需要
main(List<String> args) {
var p1 = Person("same");
// 这个省略的是new
var p2 = new Person("same");
// identical(a, b) 这个函数可以判断
// a, b两个变量是否指向同一个对象
print(identical(p1, p2));
const p3 = Person2("same");
// 这个省略的是const
const p4 = const Person2("same");
print(identical(p3, p4));
}
class Person {
String name;
Person(name) {
this.name = name;
}
}
class Person2 {
final String name;
final int age;
// const Person2(this.name);
// 相当于 多个参数用逗号隔开
const Person2(name) : this.name = name, this.age = 10;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5EKhTIPX-1582943518364)(062CF83DFF9B48FBA8347CAA4CD87BAD)]
3.2.6 工厂函数(单例模式)
Dart 提供了factory关键字, 用于通过工厂去获取对象