该部分内容主要分为四部分, 认真看, 一小时, 带你学完Dart基础语法:
测试工具我选择的是VSCode, 打开空白文件夹开始Dart基础语法的学习吧...
06. 运算符和while循环
6.1 ??运算符
- ??
主要是为其他数据赋值时的对前面参数的非空判断?? 前面的数据有值, 那么就使用?? 前面的数据,
?? 前面的数据为null, 那么就是用?? 后面的数据,
可以类比为三目运算符: var temp = name == null ? "lilei" : name;
var name = 'tom'; var temp = name ?? 'lilei'; print(name); // tom var name = null; var temp = name ?? "lilei"; print(temp); // lilei
6.2 ??=运算符
- ??=
主要是操作当前变量, 为自己赋值当原来的变量有值时, 那么??=不执行,
当原来的变量为null时, 那么就将值赋值给这个变量;
var name = "tom"; name ??= "json"; print(name); // tom var name = null; name ??= "json"; print(name); // json
定义Person类便于后测试
class Person {
String? name;
void eat() {
print("$name eating");
}
void run() {
print("$name running");
}
}
6.3 ..级联运算符
可以让你在同一个对象上连续调用多个 对象的变量或方法, 就是点语法(链式编程).
var p1 = Person()
..name = 'zhangsan'
..eat()
..run();
// 等价于
var p2 = Person();
p2.name = "lisi";
p2.eat();
p2.run();
打印结果:
zhangsan eating
zhangsan running
lisi eating
lisi running
6.4 ++/--自增/自减运算符
- 自增在后, 先赋值, 再自增,
- 自增在前, 先自增, 再赋值.
var a = 10;
var b = a++;
print("b is $b ; a is $a");
// b is 10 ; a is 11, 自增在后, 先赋值, 再自增 => b = 10. a = 11;
var c = ++a;
print("c is $c ; a is $a");
// c is 12 ; a is 12, 自增在前, 先自增, 再赋值 => a = 12. c = 12;
6.5 as、is、is! 运算符
在运行时 判断对象类型的运算符.
- as 类型转换(也用作指定 类前缀))
- is 如果对象是指定类型则返回 true
- is! 如果对象是指定类型则返回 false
var type1 = 123456;
var type2 = "123456";
print(type2 as String); // 123456
print(type1 is String); // false
print(type1 is int); // true
print(type1 is! String);// true
6.6 while循环
- 先判断再循环.
- continue; 跳过当前循环, 后面循环继续.
- break; 结束当前循环.
var flag = 5;
while (flag > 0) {
flag--;
print(flag);
}
打印结果:
4 3 2 1 0
6.7 do while循环
- 先循环再判断.
var flag = 5;
do {
flag--;
print(flag);
} while (flag > 0);
打印结果:
4 3 2 1 0
07. class类的定义
在dart 语法中, 使用class 来定义一个类:
7.1 自定义初始化
默认初始化方式:
class Person {}
在定义一些属性后 的默认的初始化方法:
class Person1 {
String? name;
Person1(String value) {
this.name = value;
}
}
或者使用 自定义初始化的语法糖(都是等价的):
class Person2 {
String? name;
int? age;
Person2(this.name, this.age);
}
7.2 类 新增属性后的初始化
当Person2 初始化方式用了一段时间后, 产品提出需要新增height字段
- 解法一: 添加可选的命名参数, 避免带来歧义.
- 需要修改原始api.
class Person3 {
String? name;
int? age;
double? height;
Person3(this.name, this.age, {this.height});
}
在使用上:
// 旧的api 不影响
var old_p = Person3('lisi', 18);
// 新的api 直接指定height 参数即可
var new_p = Person3('zhangsan', 18, height: 1.88);
- 解法二: 命名构造函数(
也是解决dart 无法重载的方案) - 直接新增api
class Person4 {
String? name;
int? age;
double? height;
Person4(this.name, this.age);
Person4.createForNameAgeHeight(this.name, this.age, this.height);
Person4.formMap(Map<String, dynamic> map) {
this.name = map["name"];
this.age = map["age"];
this.height = map["height"];
}
}
在使用上:
var old_p = Person4('lilei', 12);
var new_p1 = Person4.createForNameAgeHeight('hanmeimei', 12, 1.44);
var new_p2 = Person4.formMap({'name': 'lisi', 'age': 13, 'height': 1.55});
7.3 重写toString 打印对象属性
class Person5 {
String? name;
int? age;
double? height;
Person5.formMap(Map<String, dynamic> map) {
this.name = map["name"];
this.age = map["age"];
this.height = map["height"];
}
@override
String toString() {
// TODO: implement toString
return "my name is $name, age is $age, height is $height";
}
}
使用上:
var p = Person5.formMap({'name': 'lisi', 'age': 13, 'height': 1.55});
print(p); // my name is lisi, age is 13, height is 1.55
0.8 类的初始化列表
8.1 初始化结束的时间点
class Person {
String name;
int age;
// ❌会报错: Non-nullable instance field 'age/name' must be initialized.
// 代表在初始化Person类 之前, 要对两个属性进行初始化
// ❗️初始化完成节点: 在进入函数体之前, person类就初始化结束了, 所以报错了.
Person(String name, int age)
{ // ❗️到这个位置, 类的初始化就已经完成啦~~~
this.name = name;
this.age = age;
}
}
一般可以写成:
class Person {
String? name;
int? age;
Person(this.name, this.age);
}
// 或者
class Person1 {
String? name;
int? age;
Person1(String nameValue, int ageValue) {
name = nameValue;
age = ageValue;
}
}
// 或者, 初始化Person2 之后, 由于属性是final 修饰的, 不能二次赋值.
class Person2 {
final String name;
final int age;
Person2(this.name, this.age);
@override
String toString() {
return "$name $age";
}
}
8.2 初始化后属性的默认值处理
- 在属性后直接赋值, 函数初始化将不能够再为age属性赋值, 也就是一直都默认为10.
final int age = 10;
- 通过初始化列表设置默认值.
final String name;
final int age;
// 同样的, 外界初始化Person3 时, 只能传递一个name 属性, age永远默认为22.
Person3(this.name): age = 22;
这里面的 冒号 属性名称 = 默认值 这个代码表达式就被称作为初始化列表.
- 拓展: 外界传age 时, age为外界入参, 外界不传age时, 再为设置的默认值.
// age必须为可选的参数, 因为外界不传递时, 默认为null
Person4(this.name, {int? age}): this.age = age ?? 10;
// 完全等价于Person4 的初始化列表的方式
// 但是Person5 具有局限性, 命名可选参数只能是一个赋值操作, 且是一个确定值, 不能是语句
// 初始化列表中可以添加很多语句
Person5(this.name, {this.age = 10});
// 多属性的初始化列表写法
Person6(this.name, {int? age, double? height}): this.age = age ?? 10, this.height = height ?? 1.88;
09. 重定向构造函数
- 初始化p的时候, age没有默认值
class Person1 {
String? name;
int? age;
// 初始化p的时候, age没有默认值
Person1(this.name);
}
void main(List<String> arguments) {
var p1 = Person1('zhangsan');
print('${p1.name} ${p1.age}'); // zhangsan null
}
- 初始化p的时候,想给age一个默认值
class Person2 {
String? name;
int? age;
// 初始化p的时候, age没有默认值
Person2(this.name): age = 10;
}
void main(List<String> arguments) {
var p2 = Person2('zhangsan');
print('${p2.name} ${p2.age}'); // zhangsan 10
}
- 初始化p的时候,想给age一个默认值 =>
构造函数重定向: 一个初始化函数调用另一个初始化函数
class Person3 {
String? name;
int? age;
Person3(String name) : this._internal(name, 17);
// 构造函数重定向 (internal: 内部的)
Person3._internal(this.name, this.age);
}
void main(List<String> arguments) {
var p3 = Person3('zhangsan');
print('${p3.name} ${p3.age}'); // zhangsan 17
var p4 = Person3._internal('lilei', 22);
print('${p4.name} ${p4.age}'); // lilei 22
}
10. 常量构造函数
- 属性final 修饰
- 构造函数使用const 修饰
- 外界使用对象时, 使用const 声明
class Person {
final String name;
const Person(this.name);
}
void main(List<String> arguments) {
const p1 = Person("ll");
const p2 = Person("ll");
print(identical(p1, p2)); // true: 指向了同一块内存
}
- 常量构造函数里面, 一般只能存在一个构造函数, 不存在多个
class Person {
final String name;
// ❌报错: All final variables must be initialized,
const Person(this.name);
final String age;
// ❌报错: The unnamed constructor is already defined.
// 1. dart不支持重载;
// 2. 参数不同, final必须全部初始化, 不符合标准, const Person(this.name)报错了;
const Person(this.name, this.age);
}
结语
路漫漫其修远兮,吾将上下而求索~
.End