首页 > 其他分享 >Flutter 陈航 06-Dart 基础语法

Flutter 陈航 06-Dart 基础语法

时间:2022-12-04 15:45:06浏览次数:64  
标签:运算符 06 name Point printInfo Dart 陈航 print

本文地址


目录

目录

06 | 基础语法与类型变量

Dart 的变量与类型

和绝大多数编译型语言一样,Dart 要求以 main 函数作为执行的入口。

在 Dart 中,我们可以用 var 或者具体的类型来声明一个变量

  • 当使用 var 定义变量时,表示类型是交由编译器推断决定的
  • 也可以用静态类型去定义变量,这样编辑器和编译器就能使用这些静态类型,向你提供代码补全或编译警告的提示了

未初始化的变量的值都是 null

Dart 是类型安全的语言,并且所有类型都是对象类型,都继承自顶层类型 Object,函数和 null 也都是继承自 Object 的对象。

num、bool 与 String

Dart 的数值类型 num 只有两种子类:

  • 64 位 int,代表整数类型
  • 符合 IEEE 754 标准的 64 位 double,代表浮点数类型
main() {
  print("$age - $size - ${size.round()} - $hex - $e"); // 30 - 5.678 - 6 - 255 - 1200.0
}

var age = 30;
double size = 5.678;
int hex = 0xFF;
double e = 1.2e3;

dart:math 库提供了诸如三角函数、指数、对数、平方根等高级函数。

在 Dart 里,只有两个对象具有 bool 类型:true 和 false,它们都是编译时常量。

Dart 的 String 由 UTF-16 的字符串组成。构造字符串字面量时既能使用单引号也能使用双引号,还能在字符串中使用 ${express} 嵌入变量或表达式。

为了获得内嵌对象的字符串,Dart 会调用对象的 toString() 方法。

可以通过三个单引号三个双引号的方式声明多行字符串。

List 与 Map

main() {
  var arr1 = ["Tom", "Andy", "Jack"]; // List<String>
  var arr2 = List.of([1, 2, 3]); // List<int>
  arr1.add("BQT");
  arr2.insert(0, 30);
  print(arr1); // [Tom, Andy, Jack, BQT]
  arr2.forEach((v) => print(v));

  var map1 = {"name": "Tom", 'sex': 'male'}; // Map<String, String>
  var map2 = new Map();
  map1['name'] = 'BQT';
  map2['sex'] = '男';
  print(map1); // {name: BQT, sex: male}
  map2.forEach((k, v) => print('${k}: ${v}'));
}

Dart 会自动根据上下文对集合类型进行推断,所以后续往容器内添加的元素也必须遵照这一类型。

如果编译器自动推断的类型不符合预期,我们可以在声明时显式地把类型标记出来,不仅可以让代码提示更友好一些,还可以让静态分析器帮忙检查字面量中的错误,解除类型不匹配带来的安全隐患或是 Bug。

main() {
  var arr1 = <String>["Tom", "Andy", "Jack"];
  var arr2 = List<int>.of([1, 2, 3]);
  var map1 = <String, String>{"name": "Tom", 'sex': 'male'};
  var map2 = new Map<String, String>();

  print(arr1.runtimeType); // List<String>
  print(map1.runtimeType); // _InternalLinkedHashMap<String, String>
  print(arr2 is List<int>); // true
  print(map2 is Map<String, String>); // true
}

常量定义

  • const,表示变量在编译期间即能确定的值 -- 编译期常量
  • final 表示变量可以在运行时确定值,而一旦确定后就不可再变 -- 运行时常量
import 'dart:math';

main() {
  const size = 3; // 编译期常量,当然也可以使用 final 定义
  final count = Random().nextInt(10); // 运行时常量,不能使用 const 定义
}

07 | 函数、类与运算符

函数

在 Dart 中,所有类型都是对象类型,函数也是对象,它的类型叫作 Function

main() {
  bool isZero(int number) => number == 0;

  void printInfo(int number, Function check) => print("$number is Zero: ${check(number)}");

  Function f = isZero;
  printInfo(10, isZero); // 10 is Zero: false
  printInfo(0, f); // 0 is Zero: true
}

可选命名参数和可选参数

Dart 认为重载会导致混乱,因此从设计之初就不支持重载,而是提供了可选命名参数和可选参数。

具体方式是,在声明函数时:

  • 可选命名参数:给参数增加{},必须显式声明参数名,参数位置无所谓 -- 可理解为 map
  • 可忽略的参数:给参数增加[],这些参数可以忽略,但必须按顺序摆放 -- 可理解为 链表(LinkedList)

在使用这两种方式定义函数时,我们还可以在参数未传递时设置默认值

main() {
  // 可选命名参数
  String f1({int? age, String? name}) => "$age,$name ";
  print(f1(age: 30, name: "BQT") + f1(age: 30) + f1(name: "BQT") + f1()); // 30,BQT 30,null null,BQT null,null

  String f2({int? age = 0, String name = "xxx"}) => "$age,$name ";
  print(f2(age: 30, name: "BQT") + f2(age: 30) + f2(name: "BQT") + f2()); // 30,BQT 30,xxx 0,BQT 0,xxx

  // 可忽略的参数
  String f3(int? age, [String? name, bool meal = true]) => "$age,$name,$meal ";
  print(f3(30, "BQT", false) + f3(30, "BQT") + f3(30)); // 30,BQT,false 30,BQT,true 30,null,true 
}

类的定义

Dart 中并没有 public、protected、private 这些关键字,我们只要在声明变量与方法时,在前面加上 _ 即可作为 private 方法使用。如果不加 _,则默认为 public。

注意,_ 的限制范围并不是类访问级别的,而是库(package)访问级别。

class Point {
  num x, y;
  Point(this.x, this.y); //语法糖,等同于在函数体内执行 this.x = x; this.y = y;
  void printInfo() => print('($x, $y)');

  static num factor = 0;
  static void printF() => print('$factor');
}

main() {
  Point.factor = 8;
  Point.printF();

  Point point = new Point(100, 200); // 关键字 new 可以省略
  point.printInfo();
}

命名构造函数

除了可选命名参数和可选参数之外,Dart 还提供了命名构造函数的方式,使得类的实例化过程语义更清晰。

此外,与 C++ 类似,Dart 支持初始化列表。在构造函数的函数体真正执行之前,你还有机会给实例变量赋值,或者重定向至另一个构造函数。

class Point {
  num x, y, z;

  Point(this.x, this.y, [this.z = 8]); // 可选参数
  Point.bottom(num x) : this(x, x); // 命名构造函数 + 重定向
  Point.def() : x=0, y=0, z=0; // 命名构造函数 + 初始化列表

  void printInfo() => print('($x,$y,$z)');
}

main() {
  Point(6, 6).printInfo(); // (6,6,8)
  Point(6, 6, 6).printInfo(); // (6,6,6)
  Point.bottom(5).printInfo(); // (5,5,8)
  Point.def().printInfo(); // (0,0,0)
}

继承与接口实现

在 Dart 中,你可以对同一个父类进行继承或接口实现:

  • 继承父类意味着,子类由父类派生,会获取父类的成员变量和方法实现,可以根据需要覆写构造函数及父类方法
  • 接口实现意味着,子类获取到的仅仅是接口的成员变量符号和方法符号,需要重新实现成员变量和接口方法
class Point {
  num x = 0, y = 0;
  void printInfo() => print('($x,$y)');
}

// 继承
class Vector extends Point {
  @override
  void printInfo() => print('(${x + 100},$y)'); // 覆写了父类实现
}

// 接口实现,注意,这里会将 class Point 当做接口
class Coordinate implements Point {
  @override
  num x = 0, y = 0; // 所有成员变量【必须】重新声明【并实现】

  @override
  void printInfo() => print('(${x + 10},${y + 10})'); // 成员函数也必须重新声明并实现
}

接口实现时,成员变量和方法必须重新声明并实现,否则提示 Try implementing the missing methods, or make the class abstract

main() {
  Vector()
    ..x = 1
    ..y = 2 //级联运算符,等同于 xxx.x=1; xxx.y=2;
    ..printInfo(); // (101,2)

  var yyy = Coordinate()
    ..x = 1
    ..y = 2
    ..printInfo(); // (11,12)
  print(yyy is Point); //true
}

混入 Mixin

除了继承和接口实现之外,Dart 还提供了另一种机制来实现类的复用,即 混入 Mixin。混入可以被视为具有实现方法的接口,不仅可以解决 Dart 缺少对多重继承的支持问题,还能够避免由于多重继承可能导致的歧义(菱形问题)。

备注:继承歧义,也叫菱形问题,是支持多继承的编程语言中一个相当棘手的问题。
当 B 类和 C 类继承自 A 类,而 D 类继承自 B 类和 C 类时会产生歧义。如果 A 中有一个方法在 B 和 C 中已经覆写,而 D 没有覆写它,那么 D 继承的方法的版本是 B 类,还是 C 类的呢?

要使用混入,只需要 with 关键字即可。

class MixinP with Point {}

main() {
  var yyy = MixinP()
    ..x = 100
    ..printInfo(); // (100,0)

  print(yyy is Point); //true
}

通过混入,一个类可以以非继承的方式使用其他类中的变量与方法。

Flutter 很多地方都用到了混入

顺序: extends before with before implements

class E extends A with B, C implements C, D {} // extends > with > implements
class F extends A implements B, C {}
class G extends A with B, C {}       // 可以同时混入多个类
class H with B, C implements C, D {} // 可同时 with / implements 同一个 class

注意:声明构造函数的类无法被别的类混入。The class 'XXX' can't be used as a mixin because it declares a constructor. (Documentation)

运算符

Dart 多了几个额外的运算符,用于简化处理变量实例为 null 的情况。

  • ?. 运算符:a?.xx() ,表示 a 为 null 的时候跳过,避免抛出异常
  • ??= 运算符:a ??= value,如果 a 为 null,则给 a 赋值 value,否则跳过
  • ?? 运算符:a ?? b,如果 a 不为 null,则返回 a,否则返回 b

对于系统的运算符,一般情况下只支持基本数据类型和标准库中提供的类型。而对于用户自定义的类,如果想支持基本操作,比如比较大小、相加相减等,则需要用户自己来定义关于这个运算符的具体实现。

Dart 提供了类似 C++ 的运算符覆写机制,使得我们可以覆写或者自定义运算符。

class Vector {
  num x, y;
  Vector(this.x, this.y);

  Vector operator +(Vector v) => Vector(x + v.x, y + v.y); // 自定义 + 运算符,实现向量相加
  bool operator ==(dynamic v) => x == v.x && y == v.y; // 覆写 == 运算符,判断向量相等

  String info() => "($x,$y)";
}

main() {
  var x = Vector(3, 3);
  var y = Vector(2, 2) + Vector(1, 1);
  print(y.info()); // (3,3)
  print(x == y); // true
}

operator 是 Dart 的关键字,与运算符一起使用,表示一个类成员运算符函数。在理解时,我们应该把 operator 和运算符作为整体,看作是一个成员函数名。

问:在覆写相等运算符时为何需要传入 dynamic 变量,而不能传入 Vector 呢?
答:因为 operator== 是继承自 Object 类,这个类的参数声明就是 dynamic

08 | 综合案例

class Meta {
  double price;
  String name;

  Meta(this.name, this.price); // 成员变量初始化
}

class Item extends Meta {
  Item(name, price) : super(name, price);

  Item operator +(Item item) => Item("BQT", price + item.price); // 运算符重载
}

abstract class PrintHelper {
  void printInfo() => print(getInfo());

  String getInfo();
}

// 以非继承的方式复用另一个类的成员变量及函数
class ShoppingCart extends Meta with PrintHelper {
  DateTime date;
  String? code;
  late List<Item> bookings; // add an init expression, or init in constructor, or mark it 'late'

  double get price => bookings.reduce((v, e) => v + e).price; // 归并求和

  ShoppingCart({name}) : this.withCode(name: name, code: null); // 重定向

  ShoppingCart.withCode({name, this.code})
      : date = DateTime.now(),
        super(name, 0); // 调用父类初始化方法

  @override
  getInfo() => '''
购物车信息:
-----------------------------
  用户名: $name
  优惠码: ${code ?? "code 为空"}
  总价: $price
  Date: ${date.getDate()}
-----------------------------
''';
}

// 扩展方法 https://blog.csdn.net/weixin_43836055/article/details/126073256
extension DateTimeExt on DateTime {
  String getDate() => "$year.$month.$day $hour:$minute:$second";
}

void main() {
  ShoppingCart.withCode(name: '张三', code: '123456')
    ..bookings = [Item('苹果', 6.0), Item('鸭梨', 3.0)]
    ..printInfo();

  ShoppingCart(name: '李四')
    ..bookings = [Item('香蕉', 8.0), Item('西瓜', 5.0)]
    ..printInfo();
}

2022-12-4

标签:运算符,06,name,Point,printInfo,Dart,陈航,print
From: https://www.cnblogs.com/baiqiantao/p/16949985.html

相关文章

  • 2022-2023-1 20221306《计算机基础与程序设计》第十四周学习总结
    作业信息班级链接:https://edu.cnblogs.com/campus/besti/2022-2023-1-CFAP作业要求: https://www.cnblogs.com/rocedu/p/9577842.html#WEEK14作业目标:《C语言程序设计》第......
  • 顺序表-00006-判空、判满
    顺序表结构定义typedefintseqType; //定义顺序表数据类型//定义顺序表的结构体typedefstructt_sList{ seqType*pbase; //表基址 intcapacity; //表......
  • docker安装minio 2022-06-17T02-00-35Z
    minio版本太多,这个版本比较稳定dockerpull minio/minio:RELEASE.2022-06-17T02-00-35Zdockerrun-d-p3001:9000-p3002:9001--nameminio\-e"MINIO_ROOT_USER......
  • Flutter 陈航 05-工程结构 示例项目 声明式
    本文地址目录目录目录05|Flutter是如何运行在原生系统上的计数器示例工程工程结构工程代码应用的整体结构MyAppMyHomePage页面布局及交互逻辑ScaffoldsetState代码流......
  • poj 1061f「一本通 6.4 例 1」青蛙的约会
    sloj P10209.「一本通6.4例1」青蛙的约会题目描述原题来自:POJ1061两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面。它们很高兴地发现它们住在同一条......
  • Flutter 陈航 课程介绍 环境 Dart 语言概览
    本文地址目录目录目录Flutter核心技术与实战开篇词|大前端都应该学习Flutter01|预习篇·搭建Flutter环境02|预习篇·Dart语言概览Dart是什么?Dart的特......
  • Threejs教程-06-光源和材质
    接上节,讲了Threejs05”。本节具体讲解一些Threejs的知识点。首先有条件的话搞个服务器,这里还是用的三丰云免费云服务器,免费虚拟主机在这里不适用就不介绍了,安装的Centos7......
  • 搞定Dart的异步
    一.Dart的异步模型我们先来搞清楚Dart是如何搞定异步操作的1.1.Dart是单线程的1.1.1.程序中的耗时操作开发中的耗时操作:在开发中,我们经常会遇到一些耗时的操作......
  • 【干货】超详细!TPC7062封装MQTT协议教程
    【干货】超详细!TPC7062封装MQTT协议教程一.功能简介通过将报文分解为16进制格式的字符串(比如:101C00044D51545404C2),再通过TPC-7062进行组包,利用串口服务器的TCP/IP协议栈连接......
  • node-sass升级为dart-sass
    卸载node-sassnpmuninstallnode-sass安装dart-sassnpminstallsasssass-loader-D在选择dart-sass版本的时候建议低一些,我一开始比较高"sass":"^1.32.13",,导致我......