• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

跨平台技术篇 - Dart 语法全解析 (上)

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

学习 Flutter,必须得掌握 Dart 语言,这篇文章就来整理一下 Dart 的语法,由于内容较多,所以分成上下两篇。

 

目录:

  1. Dart 简介
  2. Dart 开发环境
  3. 注释
  4. 关键字
  5. 变量和常量
  6. 特殊数据类型
  7. 运算符
  8. 流程控制语句
  9. 异常

 

 

1. Dart 简介

Dart 是面向对象的、类定义的、单继承的语言。它的语法类似 C 语言,可以转译为 JavaScript,支持接口 (interfaces)、混入 (mixins)、抽象类 (abstract classes)、具体化泛型 (reified generics)、可选类型 (optional typing) 和 sound type system 。

Dart 语言是使用 Flutter 框架开发时候必备的语言,Flutter 是一个跨平台的框架,一套代码就可以完美实现 Android 和 iOS 两个平台,适配也很不错,Dart 语言很友好,和 Java 很类似,学习成本也是很低的。

学习 Dart 语言,必须将以下的概念熟记于心:

  • 在 Dart 语言中,一切皆为对象。所有的对象都是一个类的实例。甚至整数、函数、null 也看做是对象。所有的对象都继承于Object 类。
  • 尽管 Dart 是强类型语言,但是变量的类型指定不一定要标明,因为 Dart 可以推断出它的类型。比如说变量 number 就可以被推测出是 int 类型。如果你想明确表示这个变量不想被任何一个类型指定,那就使用特殊类型 dynamic 来表示。
  • Dart 语言支持通用类型,比如 List<int> 表示整数集列表,List<dynamic> 表示元素集为任意类型对象的列表。
  • Dart 支持顶级函数 (如 main()),以及绑定到类或对象的函数(分别是静态方法和实例方法)。你还可以在函数中创建函数(嵌套函数或本地函数)。
  • 类似的,Dart 支持顶级变量,以及绑定到类或对象 (静态和实例变量) 的变量。实例变量有时称为字段或属性。
  • 有别于 Java 语言,Dart 语言中没有 public、protected 和 private 这些关键字。在 Dart 里面,如果标识符以下划线 (_) 开头,那么它对其库是私有的。
  • Dart 里面的标志符号由 _、字母和数字组成,只能以 或者字母开头。
  • Dart 既有表达式(具有运行时值),也有语句(不具有运行时值)。比如传统的表达式 condition ? expr1 : expr2 会有一个值,expr1 或者 expr2。相比较于 if-else 语句,则没有值。一个语句通常包括一个或者多个表达式,但是一个表达式不能直接包含一个语句。
  • Dart 工具会向你发出两种类型问题:警告和错误。警告则是指出你的代码存在问题,但是不会阻止你正在执行的程序。错误则会发生在编译时和运行时。编译时的错误会完全阻止你代码的运行。运行时错误导致代码执行时引发异常。

 

 

2. Dart 开发环境

我这边的 OS 是 Mac,IDE 是 Intellij Idea。

 

  • 2.1 Dart SDK
$ brew tap dart-lang/dart
$ brew install dart

安装成功后:

Please note the path to the Dart SDK:
  /usr/local/opt/dart/libexec
==> Summary
????  /usr/local/Cellar/dart/2.2.0: 340 files, 281.4MB, built in 16 seconds

可以看到 Dart SDK 被安装到了 /usr/local/opt/dart/libexec 目录。

 

  • 2.2 Intellij Idea dart 插件

教程:https://blog.csdn.net/cekiasoo/article/details/82735312,安装完成后,IDE 会自动重启。

 

  • 2.3 新建 Dart 项目

New Project,选择 Dart,可以看到它需要 Dart SDK 的路径:

然后下一步,填写项目名词,存放路径,最后确定,创建完后的项目结构如下:

然后再新建一个存放代码的文件夹,可以随意命名,我这边命名为 src,再创建一个 Dart 文件:

接下来我们就来开始学习 Dart 的语法。

 

 

3. 注释

Dart 的注释分为 3 种:单行注释、多行注释、文档注释。

  • 1. 单行注释以//开头,Dart 编译器会忽略 // 和行尾之间的所有内容。
  • 2. 多行注释以/*开头,以*/结尾。介于/*和 */两者之间的内容会被编译器忽略 (除非该注释是一个文档注释)。多行注释可以嵌套。
  • 3. 文档注释以 /// 或者 /** 开头。可以通过 dartdoc 命令导出文档。

 

 

4. 关键字

Dart 的关键字一共有 56 个。

 

  • 4.1 保留字 (33个)
关键字 - - -
if superdo switch assert
else in this enum
is throw true break
new try case extends
null typedef catch var
class false void const
final rethrow while continue
finally return with for
default - - -

 

  • 4.2 内置标志符 (17 个)
关键字 - - -
abstract deferred as dynamic
covariant export external factory
get implements import library
operator part set static
typedef    

 

  • 4.3 Dart2 相对于 Dart1 新增的,支持异步功能的关键字 (6 个)
关键字 - - -
async async* await sync*
yield yield*

 

  • 4.4 和 java 相比,Dart 特有的关键字 (25 个)
关键字 - - -
deferred as assert dynamic
sync* async async* in
is await export library
external typedef factory operator
var part const rethrow
covariant set yield get
yield*    

 

 

5. 变量和常量

 

  • 5.1 变量的声明,可以使用 var、Object 或 dynamic 关键字
main() {
  // 创建变量并初始化变量实例
  var name = "kuang";
  // 如果对象不限于单一类型 (没有明确的类型),请使用 Object 或 dynamic 关键字
  Object name1 = "zhong";
  dynamic name2 = "wen";
  // 显式声明将被推断的类型
  String name3 = "ww";
}

 

  • 5.2 默认值

未初始化的变量的初始值为 null (包括数字),因此数字、字符串都可以调用各种方法。

  // 测试数字类型的初始值是什么?
  int intDefaultValue;
  // assert 是语言内置的断言函数,仅在检查模式下有效
  // 在开发过程中,除非条件为真,否则会引发异常。(断言失败则程序立刻终止)
  assert(intDefaultValue == null);
  // 打印结果为null,证明数字类型初始化值是 null
  print(intDefaultValue);

运行,最后打印输出 null。

 

  • 5.3 final 和 const 的用法

如果你从未打算更改一个变量,那么使用 final 或 const,不是 var,也不是一个类型。一个 final 变量只能被设置一次,const 变量是一个编译时常量。const 变量是隐式的 final,final 的顶级或类变量在第一次使用时被初始化。

// 可以省略 String 这个类型声明
  final name1 = "kzw";
//final String name1  = "张三";

  const name2 = "zx";
//const String name2 = "zx";

被 final 或 const 修饰的变量无法再去修改其值:

  final name1 = "kzw";
  // 这样写,编译器提示:a final variable, can only be set once
  // 一个 final 变量,只能被设置一次。
//  name1 = "kzw_1";

  const name2 = "zx";
  // 这样写,编译器提示:Constant variables can't be assigned a value
  // const 常量不能赋值
//  name2 = "zx_1";

flnal 或者 const 不能和 var 同时使用:

 // 这样写都会报错
  final var name1 = "kzw";
  const var name2 = "zx";

常量如果是类级别的,请使用 static const:

class A {

  static const speed = 100;
}

常量的运算:

  // 速度(km/h)
  const speed = 100;
  // 距离 = 速度 * 时间
  const double distance = 2.5 * speed; 

const 关键字不只是声明常数变量,你也可以使用它来创建常量值,以及声明创建常量值的构造函数。 任何变量都可以有一个常量值:

// 注意: [] 创建的是一个空的list集合
  // const []创建一个空的、不可变的列表(EIL)。
  var varList = const []; // varList 当前是一个EIL
  final finalList = const []; // finalList一直是EIL
  const constList = const []; // constList 是一个编译时常量的EIL

  // 可以更改非final,非const变量的值
  // 即使它曾经具有const值
  varList = ["haha"];

  // 不能更改final变量或const变量的值
  // 这样写,编译器提示:a final variable, can only be set once
  // finalList = ["haha"];
  // 这样写,编译器提示:Constant variables can't be assigned a value  
  // constList = ["haha"];

只要任何插值表达式是一个计算结果为 null 或数字,字符串或布尔值的编译时常量,那么文字字符串就是编译时常量:

// 这些是常量字符串
  const aConstNum = 0;
  const aConstBool = true;
  const aConstString = 'a constant string';

// 这些不是常量字符串
  var aNum = 0;
  var aBool = true;
  var aString = 'a string';
  const aConstList = const [1, 2, 3];

  const validConstString = '$aConstNum $aConstBool $aConstString';
// 这样用就会报错:Const variables must be initialized with a constant value
// const常量必须用conat类型的值初始化。
// const invalidConstString = '$aNum $aBool $aString $aConstList';

const 和 final 它们的区别在于,const 比 final 更加严格。final 只是要求变量在初始化后值不变,但通过 final,我们无法在编译时 (运行之前) 知道这个变量的值;而 const 所修饰的是编译时常量,我们在编译时就已经知道了它的值,显然,它的值也是不可改变的。

int Func() {
  // 代码
}

final int m1 = 60;
final int m2 = Func(); // 正确
const int n1 = 42;
const int n2 = Func(); // 错误

 

 

 

6. 特殊数据类型

Dart 支持以下特殊类型:

  • numbers 数字。
  • strings 字符串。
  • booleans 布尔。
  • lists list 集合 (也称为数组)。
  • maps map 集合。
  • runes 字符 (用于在字符串中表示 Unicode 字符)。

 

  • 6.1 num 数字类型

num 是数字类型的父类,有两个子类 int 和 double。num 类型包括基本的运算符,如+,-,/和*,位运算符,如>>,在 int 类中定义。如果 num 和它的子类没有你要找的东西,math 库可能会找到。比如你会发现 abs(),ceil() 和 floor() 等方法。

(1) int 类型

int 表示整数,int 默认是64位二进制补码整数,int 的取值不大于64位,具体取决于平台。编译为 JavaScript 时,整数仅限于values,可以用双精度浮点值精确表示。可用的整数值包括-253和253之间的所有整数,以及一些幅度较大的整数。这包括一些大于2^63的整数。 因此,在编译为 JavaScript 的 Dart VM 和 Dart 代码之间,int 类中的运算符和方法的行为有时会有所不同。例如,当编译为 JavaScript 时,按位运算符将其操作数截断为32位整数。

示例:

main() {
  int intNum1 = 10 ;
  // 结果是 10
  print(intNum1);
  int intNum2 = 0xDEADBEEF ;
  // 结果是 3735928559
  print(intNum2);
}

判断一个 int 值需要多少 bit (位),可以使用 bitLength,例如:

main() {
  // bitLength 返回存储此int整数所需的最小位数
  int a1 = 1; // 占了1个bit     相当于二进制数字 00000000 00000001
  int a2 = 12; // 占了4个bit    相当于二进制数字 00000000 00001100
  int a3 = 123; // 占了7个bit   相当于二进制数字 00000000 01111011
  int a4 = 1234; // 占了11个bit 相当于二进制数字 00000100 11010010
  print('${a1.bitLength}'); //  1
  print('${a2.bitLength}');  // 4
  print('${a3.bitLength}'); // 7
  print('${a4.bitLength}'); // 11
}

(2) double 类型

Dart 的 double 是 IEEE 754 标准中规定的64位浮点数。double 的最大值是:1.7976931348623157e+308,double 类里面有一个常量 maxFinite,我们通过语句 print(double. maxFinite) 可以得到 double 的最大值。如果一个数字包含一个小数,那么它就是一个 double 类型。示例如下:

main() {
  double doubleNum1 = 1.1;
  print(doubleNum1); // 结果是1.1
  double doubleNum2 = 1.42e5;
  print(doubleNum2); // 结果是142000.0
}

(3) Dart2.1 里面新增特性,当 double 的值为 int 值时,int 自动转成 double

main() {
  double test = 12; // 打印结果是12.0
  print(test);
}

(4) Dart2.1,int 也有 api 转成 double

main() {
  int test = 10;
  print(test.toDouble()); // 结果是:10.0
}

(5) Dart2.1,double 也有 api 转成 int,会把小数点后面的全部去掉

main() {
  double test2 = 15.1;
  double test3 = 15.6234;
  print(test2.toInt());// 结果是15
  print(test3.toInt());// 结果是15
}

 

  • 6.2 String 字符串类型

Dart 里面的 String 是一系列 UTF-16 代码单元。

(1) 你可以使用单引号或双引号来创建一个字符串

main() {
  String str1 = '单引号基本使用demo.';
  String str2 = "双引号基本使用demo.";
  print(str1);
  print(str2);
}

(2) 单引号或者双引号里面嵌套使用引号

单引号里面嵌套单引号,或者//双引号里面嵌套双引号,必须在前面加反斜杠。

  // 单引号里面有单引号,必须在前面加反斜杠
  String str3 = '单引号里面有单引号it\'s,必须在前面加反斜杠.';
// 双引号里面嵌套单引号(正常使用)
  String str4 = "双引号里面有单引号it's.";
// 单引号里面嵌套双引号(正常使用)
  String str5 = '单引号里面有双引号,"hello world"';
// 双引号里面嵌套双引号,必须在前面加反斜杠
  String str6 = "双引号里面有双引号,\"hello world\"";

  print(str3);// 双引号里面有单引号it's,必须在前面加反斜杠
  print(str4);// 双引号里面有单引号it's.
  print(str5);// 单引号里面有双引号,hello world"
  print(str6);//双引号里面有双引号,"hello world"

(3) 多个字符串相邻中间的空格问题

除了单引号嵌套单引号或者双引号嵌套双引号不允许出现空串之外,其余的几种情况都是可以运行的。示例如下:

  // 这个会报错
//String blankStr1 = 'hello''''world';
// 这两个运行正常
  String blankStr2 = 'hello'' ''world'; //结果: hello world
  String blankStr3 = 'hello''_''world'; //结果: hello_world
  
// 这个会报错
//String blankStr4 = "hello""""world";
  // 这两个运行正常
  String blankStr5 = "hello"" ""world"; //结果: hello world
  String blankStr6 = "hello""_""world"; //结果: hello_world

单引号里面有双引号,混合使用运行正常:

  String blankStr7 = 'hello""""world'; //结果: hello""""world
  String blankStr8 = 'hello"" ""world'; //结果: hello"" ""world
  String blankStr9 = 'hello""_""world'; //结果: hello""_""world

双引号里面有单引号,混合使用运行正常:

String blankStr10 = "hello''''world"; //结果: hello''''world
String blankStr11 = "hello'' ''world"; //结果: hello'' ''world
String blankStr12 = "hello''_''world"; //结果: hello''_''world

(4) 你可以使用相邻字符串文字或+ 运算符连接字符串

直接把相邻字符串写在一起,就可以连接字符串了:

  String connectionStr1 =  '字符串连接''甚至可以在''换行的时候进行。';
  print(connectionStr1);// 字符串连接甚至可以在换行的时候进行

用+把相邻字符串连接起来:

  String connectionStr2 =  '字符串连接'+ '甚至可以在'+ '换行的时候进行。';
  print(connectionStr2);// 字符串连接甚至可以在换行的时候进行

使用单引号或双引号的三引号:

  String connectionStr3 = ''' 
  这是用单引号创建的
  多行字符串。
  ''' ;
  print(connectionStr3);
  String connectionStr4 = """这是用双引号创建的
  多行字符串。""";
  print(connectionStr4);

打印出:

字符串连接甚至可以在换行的时候进行。
字符串连接甚至可以在换行的时候进行。
  这是用单引号创建的
  多行字符串。
  
这是用双引号创建的
  多行字符串。

(5) 关于转义符号的使用

声明 raw 字符串 (前缀为 r),在字符串前加字符 r,或者在\前面再加一个\,可以避免“\”的转义作用,在正则表达式里特别有用。举例如下:

  print(r"换行符:\n"); //这个结果是 换行符:\n
  print("换行符:\\n"); //这个结果是 换行符:\n
  print("换行符:\n");  //这个结果是 换行符:

(6) 使用 $ 可以获得字符串中的内容,使用 ${表达式} 也可以将表达式的值放入字符串中。使用 ${表达式} 时可以使用字符串拼接,也可以使用 String 类或者 Object 里面的某些方法获得相关字符串属性

使用 $+ 字符串:

  var height = 48.0;
  print('当前标题的高度是$height'); //当前标题的高度是48.0

使用 $+ 字符串,以及字符串拼接:

  String name = "张三";
  print("$name" + "是我们的部门经理"); // 张三是我们的部门经理

这里使用了字符串的拼接,以及使用了 String 类里面的 toUpperCase() 函数,把字母全部变成大写:

  String replaceStr = 'Android Studio';
  assert('你知道' +
      '${replaceStr.toUpperCase()}'
      + '最新版本是多少吗?' ==
      '你知道ANDROID STUDIO最新版本是多少吗?');

注:== 操作符测试两个对象是否相等。assert 是断言,如果条件为 true,继续进行,否则抛出异常,中断操作。

 

  • 6.3 bool 布尔类型

Dart 表示布尔值的类型叫做 bool,它有两个值,分别是:true 和 false,它们都是编译时常量。Dart 使用的是显式的检查值,检查值的类型,如下所示:

  // 检查是否为空字符串
  var emptyStr = '';
  assert(emptyStr.isEmpty);

  // 检查是否小于等于0
  var numberStr = 0;
  assert(numberStr <= 0);

  // 检查是否为null
  var nullStr;
  assert(nullStr == null);

  // 检查是否为NaN
  var value = 0 / 0;
  assert(value.isNaN);

assert 是 Dart 语言里的的断言函数,仅在 Debug 模式下有效。在开发过程中, 除非条件为真,否则会引发异常 (断言失败则程序立刻终止)。

 

  • 6.4 list 集合,也成为数组

在 Dart 中,数组是 List 对象,因此大多数人只是将它们称为 List。

创建一个 int 类型的 list:

main() {
  List list = [10, 7, 23];
  print(list);// 输出结果  [10, 7, 23]
}

要创建一个编译时常量 const 的 list,示例如下:

  List constantList = const [10, 3, 15];
  print(constantList); // 输出结果  [10, 3, 15]

注意事项:

1. 可以直接打印 list 包括 list 的元素,list 也是一个对象。但是 Java 必须遍历才能打印 list,Java 若直接打印 list,结果是地址值。
2. 和 Java 一样 list 里面的元素必须保持类型一致,不一致就会报错。
3. 和 Java 一样 list 的角标从 0 开始。

Dart 的 list 集合给我们提供了很多 api,示例如下,api 太多就不逐个展示了:

操作 代码 含义 输出结果
新增 list.add(1);print(list); 把数字1添加到list中,默认是添加到末尾 [10, 7, 23, 1]
移除 list.remove(1);print(list); 移除数字1 [10, 7, 23]
插入 list.insert(0, 5);print(list); 在索引为0的地方插入数字5 [5, 10, 7, 23]
查找某个索引的值 int value = list.indexOf(10);print(value); 查找10在list中的索引 1
判断元素是否包含 bool result = list.contains(5);print(result); 查找list中是否包含数字5 true

 

  • 6.5 map 集合

Dart 中的 map 是将键和值相关联的对象。键和值都可以是任何类型的对象,每个键只出现一次,但你可以多次使用相同的值。

(1) 创建方式

直接声明,用 {} 表示,里面写 key 和 value,每组键值对中间用逗号隔开:

main() {
  Map companys = {'first': '阿里巴巴', 'second': '腾讯', 'fifth': '百度'};
  print(companys);//打印结果 {first: 阿里巴巴, second: 腾讯, fifth: 百度}
}

先声明,再去赋值:

  Map companys1 = new Map();
  companys1['first'] = '阿里巴巴';
  companys1['second'] = '腾讯';
  companys1['fifth'] = '百度';
  print(companys1);
  //打印结果 {first: 阿里巴巴, second: 腾讯, fifth: 百度}

要创建一个编译时常量 const 的 map,请在 map 文字之前添加 const:

  final fruitConstantMap = const {2: 'apple', 10: 'orange', 18: 'banana'};
  print(fruitConstantMap);
// 打印结果{second: 腾讯, fifth: 百度, 5: 华为}

(2) 添加元素。格式: 变量名 [key] = value,其中 key 可以是不同类型

  Map companys1 = new Map();
  companys1['first'] = '阿里巴巴';
  companys1['second'] = '腾讯';
  companys1['fifth'] = '百度';
  print(companys1);
  //打印结果 {first: 阿里巴巴, second: 腾讯, fifth: 百度}
  // 添加一个新的元素,key为“5”,value为“华为”
  companys[5] = '华为';
  // 打印结果 {first: 阿里巴巴, second: 腾讯, fifth: 百度, 5: 华为}
  print(companys); 

(3) 修改元素。格式:变量名 [key] = value

例如:把 key 为 first 的元素对应的 value 改成 alibaba:

  companys['first'] = 'alibaba';
  // 打印结果 {first: alibaba, second: 腾讯, fifth: 百度, 5: 华为}
  print(companys);

(4) 查询元素

  bool mapKey = companys.containsKey('second');
  bool mapValue = companys.containsValue('百度');
  print(mapKey); //结果为:true
  print(mapValue); //结果为:true

(5) 删除元素,可以使用 map 的 remove 或者 clear 方法

  companys.remove('first');// 移除key为“first”的元素。
  print(companys);// 打印结果{second: 腾讯, fifth: 百度, 5: 华为}

  companys.clear();// 清空map集合的数据。
  print(companys);// 打印结果{}

(6) 关于map集合的小结

  • 1. 创建 map 有两种方式。
  • 2. map 的 key 类型不一致也不会报错。
  • 3. 添加元素的时候,会按照你添加元素的顺序逐个加入到 map 里面,哪怕你的 key 不连续。比如 key 分别是 1,2,4,看起来有间隔,事实上添加到 map 的时候 {1:value,2:value,4:value} 这种形式。
  • 4. 添加的元素的 key 如果是 map 里面某个 key 的英文,照样可以添加到 map 里面,比如可以为3和 key 为 three 可以同时存在。
  • 5. map 里面的 key 不能相同,但是 value 可以相同,value 可以为空字符串或者为 null。

 

  • 6.6 runes 字符 (用于在字符串中表示 Unicode 字符)

Unicode 为世界上所有的书写系统中使用的每个字母,数字和符号定义了唯一的数值。Dart 字符串是 UTF-16 代码单元的序列,所以在字符串中表达32位 Unicode 值需要特殊的语法。Unicode 代码点的常用方法是 \uXXXX,其中 XXXX 是一个4位十六进制值。例如,心形字符(♥)是\u2665。要指定多于或少于4个十六进制数字,请将该值放在大括号中。 例如,笑的表情符号是\u{1f600}。String 类有几个属性可以用来提取符文信息。 codeUnitAt 和 codeUnit 属性返回16位代码单元。以下示例说明了符文,16位代码单元和32位代码点之间的关系。

main() {
  var clapping = '\u{1f44f}';
  print(clapping);
  print(clapping.codeUnits);
  print(clapping.runes.toList());

// 使用String. fromCharCodes 显示字符图形
  Runes input = new Runes(
      '\u2665  \u{1f605}  \u{1f60e}  \u{1f47b}  \u{1f596}  \u{1f44d}');
  print(new String.fromCharCodes(input));
}

执行输出:

????
[55357, 56399]
[128079]
♥  ????  ????  ????  ????  ????

 

 

7. 运算符

运算符在每一种语言中都很常见,Dart 的运算符如下表所示:

这里不详细去讲解每个运算符的用法,这里主要讲一下 Dart 里面比较有代表性的以及有特点的一些运算符相关用法。

 

  • 7.1 ?.像.一样,但最左边的操作数可以为空

比如:Test?.funs 从表达式 Test 中选择属性 funs,除非 Test 为空 (当 Test 为空时,Test?.funs 的值为空)。

class Test {
  static int funs = 5;

  Test() {
    print('构造函数 Test');
  }

  static fun() {
    print('Test fun函数');
  }
}

main() {
  print(Test?.funs); // 打印5
}

 

  • 7.2 ..级联符号..

级联符号..允许你在同一个对象上进行一系列操作。 除了函数调用之外,还可以访问同一对象上的字段。其实相当于 Java 的链式调用。例如:

  String s = (new StringBuffer()
    ..write('test1 ')
    ..write('test2 ')
    ..write('test3 ')
    ..write('test4 ')
    ..write('test5'))
      .toString();
  print(s); // test1 test2 test3 test4 test5

 

  • 7.3 ?? 三目运算符的一种形式

expr1 ?? expr2 表示如果 expr1 非空,则返回其值,否则返回expr2的值。

//普通三元运算符
  int a = 10;
  var values = a > 5 ? a : 0;
//??操作符
  print('a ??=3 : ${a ??= 3}'); // 10

 

  • 7.4 ~/ 除法,返回一个整数结果,其实就是取商

小学都学过:被除数 ÷ 除数 = 商 ... 余数,在 Dart 里面 A ~/ B = C,这个 C 就是商,这个语句相当于 Java 里面的 A / B = C。Dart 与 Java 不同的是,Dart 里面如果使用 A / B = D 语句,这个结果计算出来的是真实的结果。示例如下:

  var result1 = 15 / 7;
  print(result1); // 结果是:2.142857...
  var result2 = 15 ~/ 7;
  print(result2); // 结果是:2

顺便提一下取模操作,在 Dart 里面 A % B = E,这个 E 就是余数,%符号表示取模,例如:

  var result3 = 15 % 7;
  print(result3); // 结果是:1

 

  • 7.5 as、is 与 is!
  • as 判断属于某种类型。
  • is 如果对象具有指定的类型,则为 true。
  • is! 如果对象具有指定的类型,则为 false。

例如:

class Test {
  static int funs = 5;

  Test() {
    print('构造函数 Test');
  }

  static fun() {
    print('Test fun函数');
  }
}

class Test2 extends Test {
  Test2() {
    print('构造函数 Test2');
  }

  void fun() {
    print('Test2 fun函数');
  }
}

main() {
  Test test = new Test();
  Test2 test2 = new Test2();

  print(test2 is Test);  // true
  print(test is! Test2);  // true

  (test2 as Test2).fun();  // Test2 fun函数
  // 相当于
  // if (test2 is Test) {
  //   test2.fun();
  // }
}

执行输出:

构造函数 Test
构造函数 Test
构造函数 Test2
true
true
Test2 fun函数

 

 

8. 流程控制语句

控制流程语句和 Java 语言差不多,有这些语句:

 

  • 8.1 if else
if(条件语句){
    内容体
}else{
内容体
}
  • 8.2 for 循环

(1) 简单 for 循环

for(初始值;判断条件;循环后的语句){
    内容体
}

例如:

main() {
  for (int i = 0; i < 10; i++) {
    print(i);
  }
}

也可以通过 for 循环内部的闭包获取索引的值:

  var array = [];
  for (var i = 0; i < 10; i++) {
    array.add(() => print(i));
  }

(2) 使用 foreach 循环,一般 List 和 Set 都可以使用 foreach 遍历元素

如果要迭代的对象是 Iterable,或者你不想知道当前的迭代次数,可以使用 foreach()方法。

var numbers = [1,2,3,4,5,6,7,8,9];
numbers.foreach((number)=> print(number));

(3) 使用 for in 循环,一般 List 和 Set 使用 for-in 遍历元素

  var list = [1, 2, 3];
  for (var data in list) {
    print(data);
  }

(4) Dart 的 for 循环里面可以使用标记:(比较有特色的地方)

Dart 的标记:标记是后面跟着冒号的标识符。带标记的陈述是以标记 L 为前缀的陈述。带标签的 case 子句是标签 L 前缀的 switch 语句中的 case 子句。标签的唯一作用是为 "break" 和 "continue" 声明提供对象。
大多数此功能与其他语言类似,因此以下大部分内容可能对读者来说都很熟悉。Dart 的 switch 声明中处理 continue 是比较独特的,所以这一部分需要一点时间去阅读和熟悉。

  • 循环 (Loops):

标签最常用作 break 和 continue 内部循环。假设你有嵌套的循环,并要跳转到 break 或 continue 到外部循环。如果没有标记,这不可能 (轻松) 实现。以下示例使用 continue 标记名称从内部循环直接跳转到外部循环的下一轮循环:

// 返回具有最小总和的内部列表(正整数)。
List<int> smallestSumList(List<List<int>> lists) {
  var smallestSum = 0xFFFFFFFF; //已知list的总和较小
  var smallestList = null;
  outer: // 这就是标记
  for (var innerList in lists) {
    var sum = 0;
    for (var element in innerList) {
      assert(element >= 0);
      sum += element;
      // 无需继续迭代内部列表。它的总和已经太高了。
      if (sum > smallestSum) continue outer; // continue 跳出到标记处(outer)
    }
    smallestSum = sum;
    smallestList = innerList;
  }
  return smallestList;
}

此函数在所有 list 中运行,但只要总和过高,就会停止累加变量。同理,可以使用 break 跳出到外部循环:

// 计算第一个非空list
List<int> firstListWithNullValueList(List<List<int>> lists) {
  var firstListWithNullValues = null;
  outer:
  for (var innerList in lists) {
    for (var element in innerList) {
      if (element == null) {
        firstListWithNullValues = innerList;
        break outer;  // break 返回到标记处
      }
    }
  }
  // 现在继续正常的工作流程
  if (firstListWithNullValues != null) {
    // ...
  }
  return firstListWithNullValues;
}
  • 跳出代码块

标记也可以用于跳出代码块。假设我们想要统一处理错误条件,但有多个条件 (可能是深度嵌套) 来揭示 (reveal) 错误。标签可以帮助构建此代码。

void doSomethingWithA(A a) {
  errorChecks: {
    if (a.hasEntries) {
      for (var entry in a.entries) {
        if (entry is Bad) break errorChecks;   // 跳出代码块
      }
    }
    if (a.hasB) {
      var b = new A.b();
      if (b.inSomeBadState()) break errorChecks;  // 跳出代码块
    }
    // 一些看起来都正常
    use(a);
    return;
  }
  // 错误的情况,执行这里的代码:
  print("something bad happened");
}

class A{
  bool hasEntries = false;
  bool hasB = true;
  List<Bad> entries = [new Bad2(),new Bad2()];
  A.b(){

  }

  bool inSomeBadState(){
    return false;
  }
  
}

void use(A a){}

abstract class Bad{}
class Bad1 extends Bad{}
class Bad2 extends Bad{}

对代码块的使用 break 指令,使得 Dart 继续执行块之后的语句。从某个角度来看,它是一个结构化的 goto,它只允许跳转到当前指令之后的嵌套较少的位置。虽然声明标签在代码块中最有用,但它们可以用在在每个语句中。例如,foo: break foo; 是一个有效的声明。请注意:continue 上面的循环可以通过将循环体包装到带标记的代码块中并使用 break 来实现。也就是说,以下两个循环是等效的:

//以下两种描述是等价的:

// 使用 continue
for (int i = 0; i < 10; i++) {
  if (i.isEven) continue;
  print(i);
}

// 使用 break.
for (int i = 0; i < 10; i++) {
  labels: {
    // isEven 当且仅当该整数为偶数时才返回true
    if (i.isEven) break labels;
    print(i);
  }
}
  • Switch 中的标记 (label)

标记也可以用于 switch 内部。Switch 中的标记允许 continue 使用其它的 case 子句。在最简单的形式中,这可以作为一种方式来实现下一个子句:

void switchExample(int foo) {
  switch (foo) {
    case 0:
      print("foo is 0");
      break;
    case 1:
      print("foo is 1");
      continue shared; // Continue使用在被标记为shared的子句中
    shared:
    case 2:
      print("foo is either 1 or 2");
      break;
  }
}

有趣的是,Dart 没有要求 continue 的目标子句是当前 case 子句之后的子句。带标记的任何 case 子句都是有效的目标。这意味着,Dart 的 switch 语句实际上是状态机 (state machines)。以下示例演示了这种滥用,其中整个 switch 实际上只是用作状态机 (state machines)。

void main() {
  runDog();
}

void runDog() {
  int age = 0;
  int hungry = 0;
  int tired = 0;

  bool seesSquirrel() => new Random().nextDouble() < 0.1;
  bool seesMailman() => new Random().nextDouble() < 0.1;

  switch (1) {
    start:
    case 0:
      print("dog 方法已经开始");
      print('case 0 ==> age: $age');
      print('case 0 ==> hungry: $hungry');
      print('case 0 ==> tired: $tired');
      continue doDogThings;

    sleep:
    case 1:
      print("sleeping");
      tired = 0;
      age++;
      if (age > 20) break;
      print('case 1 ==> age: $age');
      print('case 1 ==> hungry: $hungry');
      print('case 1 ==> tired: $tired');
      continue doDogThings;

    doDogThings:
    case 2:  
      if (hungry > 2) continue eat;
      if (tired > 3) continue sleep;
      if (seesSquirrel()) continue chase;
      if (seesMailman()) continue bark;
      print('case 2 ==> age: $age');
      print('case 2 ==> hungry: $hungry');
      print('case 2 ==> tired: $tired');
      continue play;

    chase:
    case 3:  
      print("chasing");
      hungry++;
      tired++;
      print('case 3 ==> age: $age');
      print('case 3 ==> hungry: $hungry');
      print('case 3 ==> tired: $tired');
      continue doDogThings;

    eat:
    case 4:  
      print("eating");
      hungry = 0;
      print('case 4 ==> age: $age');
      print('case 4 ==> hungry: $hungry');
      print('case 4 ==> tired: $tired');
      continue doDogThings;

    bark:
    case 5: 
      print("barking");
      tired++;
      print('case 5 ==> age: $age');
      print('case 5 ==> hungry: $hungry');
      print('case 5 ==> tired: $tired');
      continue doDogThings;

    play:
    case 6: 
      print("playing");
      tired++;
      hungry++;
      print('case 6 ==> age: $age');
      print('case 6 ==> hungry: $hungry');
      print('case 6 ==> tired: $tired');
      continue doDogThings;
  }
}

这个函数从一个switch子句跳到另一个子句,模拟了狗的生命。在 Dart 中,标签只允许在 case 子句中使用,因此我必须添加一些 case 永远不会到达的行。这个功能很酷,但很少使用。由于我们的编译器增加了复杂性,我们经常讨论它的删除。到目前为止,它已经在我们的审查中幸存下来,但我们最终可能会简化我们的规范并让用户自己添加一个 while(true) 循环(带有标记)。这个 dog 的示例可以重写如下:

var state = 0;
loop:
while (true)
  switch (state) {
    case 0:
      print("dog has started");
      state = 2; continue;

    case 1:  // sleep.
      print("sleeping");
      tired = 0;
      age++;
      // The inevitable... :(
      if (age > 20) break loop;  // 跳出循环
      // Wake up and do dog things.
      state = 2; continue;
    
    case 2:  // doDogThings.
      if (hungry > 2) { state = 4; continue; }
      if (tired > 3) { state = 1; continue; }
      if (seesSquirrel()) { state = 3; continue; }
      ...

如果状态值被命名为常量,那么它将与原始版本一样具有可读性,但不需要 switch 语句来支持状态机。

 

  • 8.3 while 和 do while
while(判断条件){
    内容体
}

do{
内容体
} while(判断条件);

 

  • 8.4 break continue

break 停止循环:

while(a>5){
  if(b>5){
  print(a);
    break;
  }
}

continue 跳到下一个循环:

while(a>5){
  if(b<10){
  print(b);
    continue;
  }
}

如果使用 Iterable (list 或者 set),则可以使用下面这种方式:

// 第一个是满足条件就进入  第二个是foreach遍历
arrays
  .when((c)=>c.counts >=5)
  .foreach((c)=>c.next());

 

  • 8.5 switch case​​​​​​​

比较 integer,string,编译时常量使用 ==。比较对象必须是同一个类的实例 (不是其子类的实例),并且该类不能重写 ==。枚举类型在 switch 也可以运行。每一条非空 case 子句以 break 结束,也可以使用其他的方式结束:continuethrow 或者 return

var command = 'OPEN';
switch (command) {
  case 'CLOSED':
    executeClosed();
    break;
  case 'PENDING':
    executePending();
    break;
  case 'APPROVED':
    executeApproved();
    break;
  case 'DENIED':
    executeDenied();
    break;
  case 'OPEN':
    executeOpen();
    break;
  default:
    executeUnknown();
}

 

  • 8.6 assert​​​​​​​

如果布尔条件为 false,则使用 assert 语句来中断正常执行。例如:

// 确保变量具有非空值 
assert(text != null);
// 确保值小于100
assert(number < 100);
// 确保这是一个 https 网址
assert(urlString.startsWith('https'));

要将消息附加到断言,请添加一个字符串作为第二个参数。

assert(urlString.startsWith('https'),'URL ($urlString) should start with "https".');

上例中 assert 的第一个参数可以是任何解析为布尔值的表达式。如果表达式的值为 true,则断言成功并继续执行。如果为false,则断言失败并抛出异常。

 

 

9. 异常

Dart 代码可以抛出并捕获异常。Exception 是指示发生意外事件的错误。如果未捕获异常,则会暂停引发异常的 isolate ,并且通常会终止 isolate 及其程序。

与 Java 相比,Dart 的所有异常都是未经检查的异常。方法不会声明它们可能引发的异常,并且你不需要捕获任何异常。

Dart 提供了 Exception 和 Error 类型,以及许多预定义的子类型。当然,你可以定义自己的 Exception。但是,Dart 程序可以抛出任何非 null 对象,作为 Exception (不仅仅是 Exception 和 Error 对象)。

 

  • 9.1 throw

以下是抛出或引发异常的示例:

throw FormatException('Expected at least 1 section');

你也可以抛出任意对象,例如:throw '格式不正确!',通常在开发中会抛出 Error 或者 Exception 类型。因为抛出异常是一个表达式,所以可以在=>语句中以及允许表达式的任何其他地方抛出异常:

void distanceTo(int i) => throw UnimplementedError();   

 

  • 9.2 try catch​​​​​​​

捕获或捕获异常会阻止异常传递 (除非你重新抛出异常),捕获异常使你有机会处理它:

try {
    breedMoreLlamas();
} on OutOfLlamasException {
    buyMoreLlamas();
}

要处理可能抛出多种类型异常的代码,可以指定多个 catch 子句。与抛出对象的类型匹配的第一个 catch 子句处理异常,如果catch 子句未指定类型,则该子句可以处理任何类型的抛出对象。你可以使用 on 或 catch 两者兼而有之,使用 on 时需要指定异常类型,使用 catch 时,你的异常处理程序需要异常对象。示例:

try {
  breedMoreLlamas();
} on OutOfLlamasException {
  // A specific exception
  buyMoreLlamas();
} on Exception catch (e) {
  // Anything else that is an exception
  print('Unknown exception: $e');
} catch (e) {
  // No specified type, handles all
  print('Something really unknown: $e');
}

你可以指定一个或两个参数 catch(),第一个是抛出的异常,第二个是堆栈跟踪 (StackTrace 对象)。示例:

try {
  // ···
} on Exception catch (e) {
  print('Exception details:\n $e');
} catch (e, s) {
  print('Exception details:\n $e');
  print('Stack trace:\n $s');
}

要部分处理异常,同时允许它传递,请使用 rethrow 关键字。示例:

void misbehave() {
  try {
    dynamic foo = true;
    print(foo++); // 运行时异常
  } catch (e) {
    print('misbehave() partially handled ${e.runtimeType}.');
    rethrow; // 允许调用者查看exception.
  }
}

void main() {
  try {
    misbehave();
  } catch (e) {
    print('main() finished handling ${e.runtimeType}.');
  }
}

 

  • 9.3 finally

无论是否抛出异常,要确保某些代码运行,请使用 finally 子句。如果没有 catch 子句匹配该异常,则在 finally 子句运行后传递异常。示例:

try {
  breedMoreLlamas();
} finally {
  // 即使抛出异常  也会执行这句代码.
  cleanLlamaStalls();
}
该finally子句在任何匹配的catch子句之后运行:
try {
  breedMoreLlamas();
} catch (e) {
    // 首先会处理异常
  print('Error: $e'); 
} finally {
  // 然后执行这句语句
  cleanLlamaStalls(); 
}

 

 


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
Dart学习之环境搭建发布时间:2022-07-18
下一篇:
Dart函数方法发布时间:2022-07-18
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap