(四)Flutter 学习之 Dart 操作符、控制流和异常处理

Flutter系列文章目录导读:

(一)Flutter学习之Dart变量和类型系统
(二)Flutter学习之Dart展开操作符 和 Control Flow Collections
(三)Flutter学习之Dart函数
(四)Flutter学习之Dart操作符、控制流和异常处理
(五)Flutter学习之Dart面向对象
(六)Flutter学习之Dart异步操作详解
(七)Flutter 学习之开发环境搭建
(八)Flutter 和 Native 之间的通信详解
(九)Android 项目集成 Flutter 模块
(十)Flutter FutureBuilder 优雅构建异步UI
更新中…


操作符

Dart 操作符和主流语言的操作符类似, 只要有一门常用语言, 对 Dart 掌握也是很快的

Dart 和以前介绍的 Kotlin 类似, 也提供操作符重载功能

算术操作符

Dart 支持下面常用的数学操作符:

操作符 意义
+ 加号
- 减号
-expr 一元操作符,负号
* 乘号
/ 除号
~/ 除号,返回一个整型
% 取余
assert(2 + 3 == 5);
assert(2 - 3 == -1);
assert(2 * 3 == 6);
assert(5 / 2 == 2.5); // Result is a double
assert(5 ~/ 2 == 2); // Result is an int
assert(5 % 2 == 1); // Remainder

需要注意的是两个整型相除是返回一个 double 类型的值,而不是像 Java 一样返回一个整型,如果需要返回整型可以使用 ~/

Dart 还支持前缀后缀自增自减操作符:

操作符 意义
++var var = var + 1 (整个表达式的值是 var + 1)
var++ var = var + 1 (整个表达式的值是 var)
–var var = var – 1 (整个表达式的值是 var – 1)
var– var = var – 1 (整个表达式的值是 var)

下面的例子完美解释了前后缀自增自减的差异:

var a, b;

a = 0;
b = ++a; // Increment a before b gets its value.
assert(a == b); // 1 == 1

a = 0;
b = a++; // Increment a AFTER b gets its value.
assert(a != b); // 1 != 0

a = 0;
b = --a; // Decrement a before b gets its value.
assert(a == b); // -1 == -1

a = 0;
b = a--; // Decrement a AFTER b gets its value.
assert(a != b); // -1 != 0

比较操作符

操作符 意义
== 相等
!= 不等
> 大于
< 小于
>= 大于等于
<= 小于等于

需要注意的是,比较断两个对象内容是否相等使用 ==, 比较两个对象是否是同一个对象使用 identical() 函数

比较两个对象内容是否相等,需要重载操作符 == , 如何重载操作符后面会介绍

类型转换和判断操作符

操作符 意义
as 类型强转
is 判断某个对象是特定类型
is! 判断某个对象不是特定类型
// 判断 emp 是否是 Person 类型
if (emp is Person) {
  // Type check
  emp.firstName = 'Bob';
}


// 将 emp 强制转换为 Person, 如果 emp 不是 Person 类型则会抛出异常
(emp as Person).firstName = 'Bob';

逻辑操作符

操作符 意义
!expr 布尔取反
|| 逻辑或
&& 逻辑与
if (!done && (col == 0 || col == 3)) {
  // ...Do something...
}

位操作符

操作符 意义
& 按位与
| 按位或
^ 按位异或
~expr 按位非
<< 左移
>> 右移

赋值操作符

操作符 意义
= 赋值(a = b)
–= a -= b 和 a = a - b 等价
/= a /= b 和 a = a / b 等价
%= a %= b 和 a = a % b 等价
>>= a >>= b 和 a = a >> b 等价
^= a ^= b 和 a = a ^ b 等价
+= a += b 和 a = a + b 等价
*= a *= b 和 a = a * b 等价
~/= a ~/= b 和 a = a ~/ b 等价
<<= a <<= b 和 a = a << b 等价
&= a &= b 和 a = a & b 等价
|= a |= b 和 a = a | b 等价

条件表达式

Dart 提供了两个操作符来简化特定情况的 if-else 语句

  • condition ? expr1 : expr2

    如果 condition 是 true, 返回 expr1, 否则返回 expr2

    var visibility = isPublic ? 'public' : 'private';
    
  • expr1 ?? expr2

    如果 expr1 不为 null, 返回 expr1, 否则返回 expr2

    String playerName(String name) => name ?? 'Guest';
    

例如我们可以把下面的函数使用条件表达式简化下:

String playerName(String name) {
  if (name != null) {
    return name;
  } else {
    return 'Guest';
  }
}

// 可以简化成如下:
String playerName(String name) => name != null ? name : 'Guest';
String playerName(String name) => name ?? 'Guest';

级联符号(…)

严格的讲, 级联符号(cascade notation) 不是一个操作符, 而是 Dart 的一个语法糖, 它不仅可以让开发者链式调用函数, 还可以链式访问属性, 所以当我们需要频繁访问某个对象的属性和函数:

class Person {
  int age = 0;
  String name = "chiclaim";

  void sayHello() {
    print("hello , my name is $name");
  }
}

main(){
  var p = Person();
  p.age = 1;
  p.name = "johnny";
  p.sayHello();
}

我们可以使用级联操作符改造成如下形势:

main() {
  Person()
    ..age = 1
    ..name = "johnny"
    ..sayHello();
}

除此以外, 还可以级联嵌套, 如:

final addressBook = (AddressBookBuilder()
      ..name = 'jenny'
      ..email = '[email protected]'
      ..phone = (PhoneNumberBuilder()
            ..number = '415-555-0100'
            ..label = 'home')
          .build())
    .build();

需要注意的是, 不能在一个 void 函数后面开始你的级联操作:

var sb = StringBuffer();
sb.write('foo')
  ..write('bar');

但是可以在调用 StringBuffer 构造函数后面开始你的级联操作:

StringBuffer()
    ..write('foo')
    ..write('bar');

其他操作符

操作符 意义
() 函数调用
[] 集合元素访问
. 成员访问
?. 成员访问, 左边的操作符可以为null, foo?.bar 如果 foo 为空那么整个表达式为null

操作符重载

Dart 提供操作符重载功能,允许开发者重载一下操作符:

操作符 操作符 操作符 操作符
< + | []
> / ^ []=
<= ~/ & ~
>= * << ==
% >>

操作符重载的语法格式为: 返回类型 operator 操作符 (参数)

下面来看下官方一个操作符重载的例子:

class Vector {
  final int x, y;

  Vector(this.x, this.y);

  // 重载 + 操作符
  Vector operator +(Vector v) => Vector(x + v.x, y + v.y);

  // 重载 - 操作符
  Vector operator -(Vector v) => Vector(x - v.x, y - v.y);

  // 重载 == 操作符
  bool operator ==(other) => other is Vector 
        && runtimeType == other.runtimeType
        && x == other.x && y == other.y;

}

void main() {
  final v = Vector(2, 3);
  final w = Vector(2, 2);

  assert(v + w == Vector(4, 5));
  assert(v - w == Vector(0, 1));
}

如果我们重载了 == 操作符, 也要重载 hashCode 函数, 类似 Java 中重载了 equals 函数,最好也重载 hashCode 函数是一样的, 因为对象的 hash 值决定对象的存储位置

@override
int get hashCode => x.hashCode ^ y.hashCode;

控制流语句

if/else

Dart 支持 if 语句和可选的 else 语句

if (isRaining()) {
    print("raining");
} else if (isSnowing()) {
    print("snowing");
} else {
    print("unknown");
}

for循环

Dart 不仅支持标准的 for 循环:

for (var i = 0; i < 5; i++) {
}

还支持 for-in 那些实现了 Iterator 接口的类(如List/Set):

var collection = [0, 1, 2];
for (var x in collection) {
  print(x); // 0 1 2
}

通过上一篇文章(三)Flutter学习之Dart函数的介绍知道

Dart Closures 能够访问自身作用域内的变量, 哪怕这个变量是外部传递给 Closure 的 如:

var callbacks = [];
for (var i = 0; i < 2; i++) {
  callbacks.add(() => print(i));
}
callbacks.forEach((c) => c());

// 输出
1
2

但是在 JavaScript 中会输出两个 2

while/do-while/break/continue

while/do-while/break/continue 和其他语言没有什么区别, 在这里就不赘述了

switch-case

Switch 语句可以接受 整型、字符串 、枚举 或者 编译时常量, 然后使用 == 进行比较, 下面看下常量的比较:

class Person {
  final String id;

  const Person(this.id);
}

const p = Person("001");
const p1 = Person("001");
const p2 = Person("003");
  
switch (p) {
    case p1:
      print(p1.id);
      break;
    case p2:
      print(p2.id);
      break;
    default:
      print("unknown");
  }

如果 case 子句不是空, 要以 break/return/throw/continue 结尾, 否则会报错

如果想要 case 子句之间 fall-through 的话, 将 case 子句为空即可

  var command = 'CLOSED';
  switch (command) {
    case 'CLOSED':
    case 'NOW_CLOSED':
      print("NOW_CLOSED");
      break;

    default:
      print("UNKNOWN");
  }

还可以使用 continue 的方式 fall-through

var command = 'CLOSED';
switch (command) {
case 'CLOSED':
  print("CLOSED");
  continue nowClosed;

nowClosed:
case 'NOW_CLOSED':
  print("NOW_CLOSED");
  break;

default:
  print("UNKNOWN");
}

// 输出

CLOSED
NOW_CLOSED

断言(assert)

在开发阶段, 我们可以使用断言语句 assert(condition, optionalMessage); 来中断程序的正常执行, 当 conditionfalse 的时候(抛出 AssertionError 异常); 如果 conditiontrue, 则继续执行程序的下一行代码. 例如:

// Make sure the value is less than 100.
assert(number < 100);

// Make sure this is an https URL.
assert(urlString.startsWith('https'));

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

断言什么时候生效呢?这取决于你使用的工具和框架:

  • Flutter 在 debug 模式启用断言
  • 仅开发阶段使用的开发工具如 dartdevc, 默认是开启断言
  • 诸如 dartdart2js 等工具支持命令行来启用断言: --enable-asserts

在生产环境的代码, 断言语句将会被忽略, 断言的 condition 表达式不会被执行,所以不用担心性能问题

异常处理

Dart 提供了 throw, rethrow, try, catch, on, finally 关键字让开发者能够抛出和捕获异常

Java 不同的是, Dart 中所有的异常都是 unchecked 异常, 也就是说编译器不会强制开发者去捕获任何异常, 除非你有这个需要

Dart 提供了两个类异常: ExceptionError, Dart 不仅可以抛出异常还可以抛出任何不为 null 的对象:

// 抛出异常
throw FormatException('Expected at least 1 section');

// 抛出不为 null 的对象
throw 'Out of llamas!';

虽然 Dart 允许我们抛出一个不为 null 的普通对象,但是官方还是建议我们抛出的异常继承自 ExceptionError

介绍完了 throw 关键字,我们来看下 catchon 和 关键字:

catchon 都是用来捕获异常:

try {
  breedMoreLlamas();
} on OutOfLlamasException {
  // 捕获一个特定的异常
  buyMoreLlamas();
} on Exception catch (e) {
  // 捕获所有继承自 Exception 的异常,并拿到异常对象
  print('Unknown exception: $e');
} catch (e) {
  // 捕获所有异常
  print('Something really unknown: $e');
}

可见, on 关键字用于指定捕获特定的异常, catch 关键字用于拿到异常对象

catch 关键字除了可以拿到异常对象, 还可以拿到异常的 堆栈 信息, 如:

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

一般情况下, 使用了 on, catch 关键字来捕获异常, 异常会停止传播, 如果需要异常继续传播可以使用 rethrow 关键字

void misbehave() {
  try {
    dynamic foo = true;
    print(foo++); // Runtime error
  } catch (e) {
    print('misbehave() partially handled ${e.runtimeType}.');
    rethrow; // Allow callers to see the exception.
  }
}

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

最后介绍 Dart 异常处理的最后一个关键字 finally

finnaly 关键字很简单 , 就是不管是否抛出异常 finally 子句一定会执行:

try {
  breedMoreLlamas();
} finally {
  // 就算抛出异常(程序中断执行), finnaly 也会先执行
  cleanLlamaStalls();
}

try {
  breedMoreLlamas();
} catch (e) {
  // 捕获异常
  print('Error: $e');
} finally {
  // 执行 finally 子句
  cleanLlamaStalls(); // Then clean up.
}

关于 Dart操作符, 控制流, 异常处理 就介绍完毕

Reference

https://dart.dev/guides/language/language-tour#operators
https://medium.com/@ayushpguptaapg/demystifying-and-hashcode-in-dart-2f328d1ab1bc


如果你觉得本文帮助到你,给我个关注和赞呗!

另外,我为 Android 程序员编写了一份:超详细的 Android 程序员所需要的技术栈思维导图

如果有需要可以移步我的 GitHub -> AndroidAll,里面包含了最全的目录和对应知识点链接,帮你扫除 Android 知识点盲区。 由于篇幅原因只展示了 Android 思维导图:
超详细的Android技术栈

发布了161 篇原创文章 · 获赞 1125 · 访问量 125万+

猜你喜欢

转载自blog.csdn.net/johnny901114/article/details/95072693