Dart语法精炼(二)

196 阅读4分钟

该部分内容主要分为四部分, 认真看, 一小时, 带你学完Dart基础语法:

测试工具我选择的是VSCode, 打开空白文件夹开始Dart基础语法的学习吧...

dart_learn的仓库

Dart语法精炼(一)

Dart语法精炼(二)

Dart语法精炼(三)

Dart语法精炼(四)

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 asisis! 运算符

在运行时 判断对象类型的运算符.

  • 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);
}

结语

路漫漫其修远兮,吾将上下而求索~

作者简书

作者GitHub

.End