Flutter学习之Dart语言基础(关键字)

Flutter日渐火爆,因此在进行Flutter学习前先学习一些其所使用的开发语言dart的基础,这篇文章主要学习了

  1. Dart的基础代码示例
  2. Dart的概念
  3. Dart的关键字
Dart的基础代码示例
// 定义一个方法。
printInteger(int aNumber) {
  print('The number is $aNumber.'); // Print to console.
}

// main入口函数。
main() {
  var number = 42; // 声明并初始化变量。
  printInteger(number); // 函数调用。
}
// 注释

dart注释方法,更多注释可以看我的另一篇文章https://www.jianshu.com/p/d1dae0d5c472

int

数据类型,更多数据类型可看https://www.dartlang.org/guides/language/language-tour#built-in-types

print()

一种方便的显示输出方式

'...' (or "...")

字符串,dart中更推荐使用'...'

$variableName (or ${expression})

字符串插值:包括字符串文字内部的变量或表达式的字符串

var

一种声明变量而不指定其类型的方法,关键字之一

Dart重要概念

当要学习dart时,请记住以下事实和概念:

  • 一切皆为对象,放在变量中的所有内容都是一个对象,每个对象都是一个class的实例,numbers,函数和null都是对象,所有对象都继承自Object类。下面给个图你看一下,没错,连int都是对象:


    int
  • 尽管Dart是强类型的,但类型注释是可选的,因为Dart可以推断类型。在上面的代码中,数字被推断为int类型。如果要明确说明不需要任何类型,请使用特殊类型dynamic。

  • Dart支持泛型类型,如List<int>(整数列表)或List<dynamic>(任何类型的对象列表)。

  • Dart支持顶级函数(例如main()),以及绑定到类或对象的函数(分别是静态和实例方法),还可以在函数内创建函数(嵌套函数或本地函数)。

  • Dart也支持顶级变量,以及绑定到类或对象的变量(静态和实例变量),实例变量有时称为字段或属性。

  • 与Java不同,Dart没有关键字public,protected和private,如果标识符以下划线(_)开头,则它代表是私有的,否则都为公有。

  • 标识符可以以字母或下划线(_)开头,后跟这些字符加数字的任意组合。

  • Dart有表达式(具有运行时值)和语句(不具有运行时值)。例如,条件表达式"条件? expr1:expr2的值为expr1或expr2"。将其与if-else语句进行比较,该语句没有任何值。语句通常包含一个或多个表达式,但表达式不能直接包含语句。

关键字

dart共有60个关键字,所以以下篇幅可能有点长

abstract2 dynamic2 implements2 show1
as2 else import2 static2
assert enum in super
async1 export2 in2 super
await3 extends is sync1
break external2 library2 this
case factory2 mixin2 throw
catch false new true
class final null try
const finally on1 typedef2
continue for operator2 var
covariant2 Function2 part2 void
default get2 rethrow while
deferred2 hide1 return with
do if set2 yield3
  • 带有上标1的关键字是上下文关键字,仅在特定位置具有含义。
  • 带有上标2的关键字是内置标识符, 为了简化将JavaScript代码移植到Dart的任务,这些关键字在大多数地方都是有效的标识符,但它们不能用作类或类型名称,也不能用作导入前缀。
  • 带有上标3的关键字为新版本中的新标识符,是与Dart 1.0发布后添加的异步支持相关的有限保留字。

详细可看下面说明

abstract

使用abstract修饰符定义抽象类即无法实例化的类,抽象类可以自定义一些接口。抽象类通常有抽象方法,下面是一个声明具有抽象方法的抽象类的示例:

// 该类声明为抽象类且不可实例化。
abstract class AbstractContainer {
  // 定义构造函数,变量,方法等...

  // 其他....  

  // 抽象方法。
  void updateChildren(); 
}

下面为实现抽象方法的例子:

//抽象类
abstract class Doer {
  void doSomething(); // 定义一个抽象方法
}

//继承抽象类实现抽象方法
class EffectiveDoer extends Doer {
  void doSomething() {
    // 实现逻辑
  }
}
dynamic

顾名思义,dynamic(动态), 直接先上代码

void judge(dynamic arg){
    if (arg is bool){
      print('arg is bool');
    } else if (arg is String){
      print('arg is String');
    } else if (arg is int){
      print('arg is int');
    } else {
      print('arg is others');
    }
}

dynamic同等于Object, 即上面代码可以等同于下面代码:

void judge(Object arg){
    if (arg is bool){
      print('arg is bool');
    } else if (arg is String){
      print('arg is String');
    } else if (arg is int){
      print('arg is int');
    } else {
      print('arg is others');
    }
}

在Dart中,dynamic和Object可表示所有类型, 这两者区别是使用dynamic可以处理更复杂的不确定类型,例如超出了Dart的类型系统,或者值来自互操作或者在静态类型系统范围之外的情况。

implements

Java中,该关键字用于实现接口类(interface), Dart中亦有相同的意思,实现接口,我们先看代码:

// Person类,包含方法greet().
class Person {
  //在该类中,属于私有,仅对当前类可见
  final _name;

  // 不是接口,只是构造函数
  Person(this._name);

  // 接口
  String greet(String who) => 'Hello, $who. I am $_name.';
}

// 实现Person类接口的类
class Impostor implements Person {
  //只是一个普通的get方法,可忽略
  get _name => '';

  //实现Person的greet方法
  String greet(String who) => 'Hi $who. Do you know who I am?';
}

//只是一个测试方法
String greetBob(Person person) => person.greet('Bob');

void main() {
  print(greetBob(Person('Kathy')));  //打印 -> Hello, Bob. I am Kathy.
  print(greetBob(Impostor())); //打印 -> Hi Bob. Do you know who I am?
}

Dart中没有Java的interface功能,如果Impostor在不继承Person类的情况想实现Person类的接口的话,可以使用implements关键字。implements可同时实现多个类的接口:

class Point implements Comparable, Location {...}
show & hide

有时候我们导入一个库,如果只想使用库的一部分,则可以有选择地导入库,例如:

// 只导入foo
import 'package:lib1/lib1.dart' show foo;
//导入整个库除了foo
import 'package:lib2/lib2.dart' hide foo;
as, is, is!

as,is,和 !is 运算符在运行时检查类型很方便

  • as: 类型转换, 也用于指定库前缀
  • is: 类似于java的instanceof
  • !is: is操作符的取反, 即不是xxx

代码示例:

if (emp is Person) {
  // 类型检查
  emp.firstName = 'Bob';
}
// 如果emp为Person,则将firstName改为Bod, 否则会在运行时期报错
(emp as Person).firstName = 'Bob';

如果导入两个具有冲突标识符(class)的库,则可以为一个或两个库指定前缀。 例如,如果library1和library2都有一个Element类,那么as可以这样使用:

import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2; //指定库的前缀为lib2

// Uses Element from lib1.
Element element1 = Element();

// Uses Element from lib2.
lib2.Element element2 = lib2.Element();
if & else

与Java或其他语言一样,Dart支持带有可选else语句的if语句:

if (isRaining()) {
  you.bringRainCoat();
} else if (isSnowing()) {
  you.wearJacket();
} else {
  car.putTopDown();
}
import

与Java一样,使用import导入其他包。例如,Dart Web应用程序通常使用dart:html库,可以这样导入:

import 'dart:html';

如果只是想导入某个包下的某个dart文件,可以这样导入:

import 'package:test/test.dart'; //指定导入test.dart(类似于Java中的test.java)
static

使用static关键字实现类范围的变量和方法

static变量(只有在使用的时候才会进行初始化):

class Queue {
  static const initialCapacity = 16;
  // ···
}

void main() {
  assert(Queue.initialCapacity == 16);
}

static方法:

import 'dart:math';

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

  static num distanceBetween(Point a, Point b) {
    var dx = a.x - b.x;
    var dy = a.y - b.y;
    return sqrt(dx * dx + dy * dy);
  }
}

void main() {
  var a = Point(2, 2);
  var b = Point(4, 4);
  var distance = Point.distanceBetween(a, b); //静态方法,不用实例化
  assert(2.8 < distance && distance < 2.9);
  print(distance);
}
assert

断言assert(条件);
如果条件为返回false,使用assert语句可以中断正常执行, 代码:

// text等于null时中断
assert(text != null);

// number > 100时中断
assert(number < 100);

// 如果urlString不是以"https"开头
assert(urlString.startsWith('https'));

如果要附加一个消息到断言,可在第二个参数输入一个字符串:

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

枚举类型(通常称为枚举或枚举)是一种特殊类型,用于表示固定数量的常量值。
使用enum关键字声明枚举类型, 例如:

enum Color { red, green, blue }

枚举中的每个值都有一个索引getter,它返回枚举声明中值的从零开始的位置。 例如,第一个值具有索引0,第二个值具有索引1:

  print('red index: \${Color.red.index}'); // -> 打印red index: 0
  print('green index: \${Color.green.index}'); // -> 打印: green index: 1
  print('blue index: \${Color.blue.index}');· //-> 打印: blue index: 2

要获取枚举中所有值的列表,可以使用以下方法:

List<Color> colors = Color.values;

您可以在switch语句中使用枚举,如果您不处理所有枚举值,您将收到警告:

var aColor = Color.blue;

switch (aColor) {
  case Color.red:
    print('Red as roses!');
    break;
  case Color.green:
    print('Green as grass!');
    break;
  default: // 没有这行代码的话,会有一个警告
    print(aColor); // 'Color.blue'
}

枚举类型具有以下限制:

1.不能子类化,混合或实现枚举。
2.无法显式实例化枚举。

for & in

可以使用标准for循环进行迭代, 例如:

var message = StringBuffer('Dart is fun');
for (var i = 0; i < 5; i++) {
  message.write('!');
}

像List和Set这样的可迭代类支持使用的for-in形式迭代:

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

等同于:

  for (int i = 0; i < list.length; i++){
    print(list[i]); // 0 1 2
  }
extend & super

使用extends来继承一个类,使用super来调用父类:

class Television {
  void turnOn() {
    _illuminateDisplay();
    _activateIrSensor();
  }
  // ···
}

class SmartTelevision extends Television {
  void turnOn() {
    super.turnOn(); //调用父类方法
    _bootNetworkInterface();
    _initializeMemory();
    _upgradeApps();
  }
  // ···
}
async & await
  • async -> 异步
  • await -> 等待

Dart库中包含许多返回Future或Stream对象的函数,关于Future和Steam会在后续进行讲解,这里暂不深究。 这些函数是异步的:它们在设置可能耗时的操作(例如I/O)后返回,而不等待该操作完成。

async和await关键字用于异步编程

async关键字修饰一个方法,要求必须返回一个Future对象,下面为代码例子:

//async关键字声明该函数内部有代码需要延迟执行
Future<String> getResult() async { 
  return await getResultFromDb(); //await关键字声明运算为延迟执行,然后返回运算结果
}

Future<String> getResultFromDb() {
  // 很多延时操作
  // 很多延时操作
  // 很多延时操作
  // 很多延时操作
  return new Future((){
      return 'This is server...';
  });
}

//打印:result = This is server...
 print(getResult().then((result){
      print('result = $result');
  }));
export

我们来看一个官方的http库的代码: http: ^0.12.0

export.png

我们可以看到该包下面有一个src目录,还有一些其他的dart文件,Dart库中,lib/src下的代码被认为是私有的, lib下的文件为对外公开即外部可以使用的,我们可以看到该http库下browser_client.dart, http.dart, io_client.dart, testing.dart是公开的API,我们拿其中一个,这里我们拿http.dart文件看看

export 'src/base_client.dart';
export 'src/base_request.dart';
export 'src/base_response.dart';
export 'src/byte_stream.dart';
export 'src/client.dart';
export 'src/exception.dart';
export 'src/multipart_file.dart';
export 'src/multipart_request.dart';
export 'src/request.dart';
export 'src/response.dart';
export 'src/streamed_request.dart';
export 'src/streamed_response.dart';

可以看到export了几个文件,即导出了这几个文件,使外部这几个文件的api,这时我们导入http来使用一下:

import 'package:http/http.dart' as http;
image.png

可以看到导入的几个文件的类都可用了,那我们再找一个没export的文件来看看外部是否可用,我们拿browser_client.dart来看看,其中有一个类:


client.png

我们在外部使用的时候:


error.png

是会报错的,因为该类并没有export,即外部不可使用。

interface

已移除

switch & case & default

Dart中的switch语句可使用整数,字符串或编译时常量, 以下为使用字符串代码示例:

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();
}
sync & yield
  • sync 同步
  • yield 生成

当我们需要懒惰地(不需要很多手动定义可迭代类时复杂的公式化代码)生成一系列值时,可以考虑使用生成器函数, Dart内置支持两种生成器函数:

  • 同步生成器:返回一个Iterable对象
  • 异步生成器:返回Stream对象

同步生成器:将函数体标记为sync *,并使用yield语句来赋值,下面例子为返回 0-n 的迭代器:

Iterable<int> naturalsTo(int n) sync* {
  print('start');
  int k = 0;
  while (k < n) yield k++;
  print('end');
}

//使用
void main() {
  var it = naturalsTo(5).iterator;
  while(it.moveNext()) {
    print(it.current);
  }
}

//打印
start
value = 0
value = 1
value = 2
value = 3
value = 4
end

调用方法naturalsTo时,会马上返回Iterable,且可以获取迭代器iterator,但是,在调用遍历之前,naturalsTo函数主体并不会立即执行,这里我们可以看到调用var it = naturalsTo(5).iterator的时候没有任何打印,并且我们可以看到,在遍历打印的时候,先调用start,当把所有值打印完了,再打印end。说明调用naturalsTo得到这个Iterable的iterator的时候,yield会在你每次调用moveNext进行遍历的时候产生一个值。当函数运行到yield的时候,yield这里声明一个求值表达式,返回值后,函数会在下一次moveNext的时候继续执行函数体。

异步生成器函数,将函数体标记为async *,并使用yield语句来传递值:

Stream<int> asynchronousNaturalsTo(int n) async* {
  int k = 0;
  while (k < n) yield k++;
}

//使用
void main() {
  asynchronousNaturalsTo(5).listen((v) {
    print(v);
  });
}

//打印
start
0
1
2
3
4
end

使用异步生成器返回数据流string,和sync*一样,调用asynchronousNaturalsTo会立即返回Stream,但是只有在listen监听数据流的时候才会调用asynchronousNaturalsTo函数体,并且通过yield声明求值表达式来计算对应的值。

如果生成器内部使用递归的话,可以使用yield *来提高其性能:

Iterable<int> naturalsDownFrom(int n) sync* {
  if (n > 0) {
    yield n;
    yield* naturalsDownFrom(n - 1);
  }
}

//使用
void main() {
  print(naturalsDownFrom(5));
}

//打印
(5, 4, 3, 2, 1)

naturalsDownFrom函数还是返回一个Iterable,当参数为5时,5 > 0时,先执行yield 5, 这时迭代器首先会产生一个值5,然后再通过yield*生成新的值,并且加到当前迭代器中。

break & continue

跳出循环

while (true) {
  if (shutDownRequested()) break;
  processIncomingRequests();
}

跳到下一个循环迭代

or (int i = 0; i < candidates.length; i++) {
  var candidate = candidates[i];
  if (candidate.yearsExperience < 5) {
    continue;
  }
  candidate.interview();
}
external

表示代码的实现由外部提供,我们定义一个类:

class Object {
  const Object();

  external bool operator ==(other);
  external int get hashCode;
  external String toString();
}

//使用
void main() {
  Object object = new Object();
  print('to string = ${object.toString()}');
}

//打印
to string = null
//但是如果我们将toString去掉的话,则会打印
to string = Instance of 'Object' //dart中默认的toString打印

external声明了这些方法需要由外部去实现,若外部没实现,则会返回null

library & part

使用library关键字可以定义一个库的名字,我们这里自定义一个库来说明一下这两个关键字:


library.png

这里分别有3个文件,我们看看:
main.dart

library main; //定义当前库的名字为main
import 'dart:math' as math; 

//声明以下两个文件属于main库
part 'test/lib/liba.dart'; 
part 'test/lib/libb.dart';

class LibMain{
    static int max(int a, int b) => math.max(a, b);

    static String getParts() => LibA.TAG + ", " + LibB.TAG;
}

liba.dart

part of main;  //声明属于main库

class LibA{
  static String TAG = 'liba';
}

libb.dart

part of main; //声明属于main库

class LibB{
  static String TAG = 'libb';
}

再导入main之后,

import 'lib/main.dart';

liba.dart和libb.dart中声明的类外部可用:


library.png

part可以将库拆分为多个Dart文件,但是建议尽量避免使用其来创建库,因为这样会使代码很难阅读和修改。建议直接直接在lib/<包名> .dart下创建一个“main”库文件,用一个main文件来管理所有公共API。

this

和Java中this相类似,用this关键字来引用当前实例

factory

用来修饰构造函数,描述该构造函数作为一个工厂构造函数功能,在实现不用总是创建新实例的构造函数的时候,可以使用factory关键字,例如下面例子中,可能从缓存中返回实例,或者子类的实例。

class Logger {
  final String name;
  bool mute = false;

  //一个维护Logger类的map
  static final Map<String, Logger> _cache =
      <String, Logger>{};

  //根据不同的name获取对应的Logger,
  factory Logger(String name) {
    if (_cache.containsKey(name)) {
      return _cache[name];
    } else {
      final logger = Logger._internal(name);
      _cache[name] = logger;
      return logger;
    }
  }

  //一个内部构造函数
  Logger._internal(this.name);

  void log(String msg) {
    if (!mute) print(msg);
  }
}

注意,使用factory修饰的构造函数不能使用this,类似于在Java的static函数中,不能使用this

mixin & with & on

Dart 2.1中引入了对mixin关键字的支持, 我们可以看到官方的描述:Mixins是一种在多个类层次结构中重用类代码的方法。关键信息:

  • 多个类层次结构
  • 重用类代码

我这里只简单描述下该关键字的作用和使用方法:

  • mixin字面意思为混入的意思,要使用mixin的话,需要使用with关键字,后跟一个或多个mixin的名称, 可视为混入多个类
  • 有时候我们需要创建一个类,这个类需要使用不同类的不同的方法的时候,就需要使用mixin方法,,因为Dart中只能继承一个类,而且使用接口的话,必须在其他类也实现这个接口,下面我们使用我们看下下面的例子:
//程序员喜欢写代码
class Programmer{
  code(){
    print('I am a programmer, i like coding.');
  }
}

//歌唱家喜欢唱歌
class Singer{
  singing(){
    print('I am a singer, i like singing.');
  }
}

//既爱编码,也爱唱歌
class Mixin with Programmer, Singer{

}

void main() {
  Mixin mixin = Mixin();
  mixin.code();
  mixin.singing();
}

//打印:
I am a programmer, i like coding.
I am a musician, i like singing.

注意:这里类Programmer和Singer不能声明构造函数包括命名函数:


mixin.png

当我们使用mixin调用不同类相同接口结果会是怎样呢,我们看下面代码:

class A {
  name(){
    print('I am a student.');
  }
}

class B{
  name(){
    print('I am a teacher.');
  }
}

class AB with A, B{

}

class BA with B, A{

}

void main() {
  new AB().name();

  new BA().name();
}

//打印:
I am a teacher.
I am a student.

可以看到接口相同的情况这里是name,最终会调用with的最后一个类的接口。

如果我们需要限定,什么类才能被混入,可以使用mixin+on的方法限定:

//mixn定义了类Flutter要求只有实现Programmer类的才能被混入
mixin Flutter on Programmer{
  flu(){
    print('This is in flutter.');
  }
}

//会如下面图片报错
//class A  with Flutter{
//}

//可以混入
class B extends Programmer with Flutter{
}

new B ().flu();  //打印This is in flutter.
mixin.png

最后,关于mixin更详细的解释可以参考:

throw

抛出异常:

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

也可以抛出任何任意对象异常:

throw 'Out of llamas!';
throw.png
class Throw{
  @override
  String toString() {
    return 'Are you ok?';
  }
}

void main() {
  throw new Throw();
}
throw.png
try & catch & finally & on & rethrow

异常捕获:

//普通使用
void main() {
  try{
    throw "You are wrong.";
  }catch (e){
    print('catch exception: '+e);
  }
}

//打印:
catch exception: You are wrong.

使用on可以捕获某种异常

class ExceptionA{
  @override
  String toString() {
    return 'This is exception a.';
  }
}

class ExceptionB{
  @override
  String toString() {
    return 'This is exception b.';
  }
}

throwCatchException(Object object){
  try{
    throw object;
  } on ExceptionA{ //指定某种异常类
    print("It's exception a.");
  } on ExceptionB catch(e){  //指定某种异常类并获取异常对象
    print(e);
  } on Exception catch (e){
    print(e);
  }
}

void main() {
  throwCatchException(new ExceptionA());
  throwCatchException(new ExceptionB());
  throwCatchException(new Exception(''));
}
//打印:
It's exception a.
This is exception b.
Exception: 

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

void main() {
  try{
    throw 'This is a exception.';
  }catch (e, s){
    print('e ${e}');
    print('s ${s}');
  }
}
//打印:
e This is a exception.
s #0      main (file:///E:/flutter/projects/flutter/test/test.dart:5:5)
#1      _startIsolate.<anonymous closure> (dart:isolate/runtime/libisolate_patch.dart:289:19)
#2      _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:171:12)

如果想异常可传播, 使用rethrow接口

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

void main() {
  try {
    misbehave();
  } catch (e) {
    print('main() finished handling ${e.runtimeType}.');
  }
}
//打印:
misbehave() partially handled NoSuchMethodError.
main() finished handling NoSuchMethodError.

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

class ExceptionA{
  @override
  String toString() {
    return 'This is exception a.';
  }
}

class ExceptionB{
  @override
  String toString() {
    return 'This is exception b.';
  }
}

finallyMethod(){
  print('finally method.');
}

void main() {
  try {
    throw new ExceptionA();
  } on ExceptionB catch (e) {
    print(e);
  } finally{
    finallyMethod();
  }
}

//打印:
finally method.
Unhandled exception:
This is exception a.
#0      main (file:///E:/flutter/projects/flutter/test/test.dart:21:5)
#1      _startIsolate.<anonymous closure> (dart:isolate/runtime/libisolate_patch.dart:289:19)
#2      _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:171:12)
false & true

为了表示布尔值,Dart有一个名为bool的类型, 只有两个对象具有bool类型: true和false,它们都是编译时常量

new

创建类实例

class

声明一个类

final & const

final声明一个变量只能初始化一次,和java用法相同

const声明一个是编译时常量的变量, 常量变量不能进行赋值,如果const变量在类级别,则将其标记为static const。
当我们想让一个变量不被改变,可以声明为const变量。

typedef

typedef用于给函数类型指定名称,因为在Dart中,函数也是一个对象,通常用Function泛指所有函数,我们来看一下下面的例子(没有使用函数别名):


class SortedCollection {
  Function compare;
  
  //这里需要传递一个返回值为int,参数为(Object, Object)的函数
  SortedCollection(int f(Object a, Object b)) {
    compare = f;
  }
}

//一个返回值为int,参数为(Object, Object)的函数
int sort(Object a, Object b) => 0;

void main() {
  SortedCollection coll = SortedCollection(sort);

  // 我们都知道compare是一个函数
  // 但是我们知道它是什么类型的函数吗,意味着我们只知道它是一个函数,但是是什么类型的函数我们不知道
  assert(coll.compare is Function); // 这里毫无疑问是true, 即不会中断执行
}

我们可以用typedef来声明一个函数类型:

 //定义一个函数类型为compare,其类型为
typedef Compare = int Function(Object a, Object b);

class SortedCollection {
  Compare compare;

  SortedCollection(this.compare);
}

//一个返回值为int,参数为(Object, Object)的函数即类型为Compare的函数
int sort(Object a, Object b) => 0;

void main() {
  SortedCollection coll = SortedCollection(sort);
  assert(coll.compare is Function);  //True
  assert(coll.compare is Compare); //True
}

目前typedef只能用于声明函数类型,

operator

如果你想定义一个Vector类(向量类),可以使用以下运算符:

< + | []
> / ^ []=
<= ~/ & ~
>= * << ==
- % >>

下面为一个覆盖+和 - 运算符的类的示例:

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

// Operator == and hashCode not shown. For details, see note below.
// ···
}

void main() {
  final v = Vector(2, 3);
  final w = Vector(2, 3);
  final vw = v + w;
  final ww = v - w;

  print('vw -> (${vw.x}, ${vw.y})');
  print('ww -> (${ww.x}, ${ww.y})');
}

//打印:
vw -> (4, 6)
ww -> (0, 0)
var

使用var声明一个变量但是不指定其类型

var name = 'Bob'; //声明一个变量为字符串变量

但是一旦声明赋值了为一个类型候,不能再分配给另一个类型


var.png
covariant

我们在继承一个类的时候,在重构一个方法时,强制将其参数由父类缩窄成其子类的话,会提示错误,例子:

//定义一个Animal类,其函数chase参数为Animal
class Animal {
  void chase(Animal x) {}
}

class Mouse extends Animal {
  getName(){
    return 'mouse';
  }
}

class Cat extends Animal {
  //强制将chase函数的Animal参数缩窄成Mouse 

  void chase(Mouse mouse) {
    print('cat chase ${mouse.getName()}');
  }
}

//报错, 提示重写类型不匹配
test/test.dart:12:20: Error: The parameter 'mouse' of the method 'Cat::chase' has type #lib1::Mouse, which does not match the corresponding type in the overridden method (#lib1::Animal).
Change to a supertype of #lib1::Animal (or, for a covariant parameter, a subtype).
  void chase(Mouse mouse) {
                   ^
test/test.dart:2:8: Context: This is the overridden method ('chase').
  void chase(Animal x) {}
       ^

使用covariant关键字后:

class Animal {
  void chase(Animal x) {}
}

class Mouse extends Animal {
  getName(){
    return 'mouse';
  }
}

class Cat extends Animal {
  void chase(covariant Mouse mouse) {
    print('cat chase ${mouse.getName()}');
  }
}

void main(){
  new Cat().chase(new Mouse());
}
//打印
cat chase mouse

covariant字义为协变,即我和编译器协商,这个参数缩窄变化是我故意这样做的,你别抛异常了。

Function

Dart是一种真正的面向对象语言,因此即使是函数也是对象并且具有类型Function。 这意味着函数可以分配给变量或作为参数传递给其他函数。

//定义一个add函数
int add(int a, int b) => a+b;

handle(Function function){
  print('handle: ${function(1, 2)}'); //打印function的结果
}

void main(){
  handle(add); //调用handle并传递add函数
}
//打印
handle: 3
void

在Dart 1中,void仅可用作函数的返回类型(例如void main()),但在Dart 2中它已被推广,并且可在其他地方使用, 例如Future <void>

void类型不能用于任何东西,且将某些东西分配给void类型是无效的:

void foo() {}
void main() {
  var bar = foo(); // 无效
}

The expression here has a type of 'void', and therefore cannot be used. -> 此表达式的类型为“void”,无法使用

void.png

在函数中,表示该函数无需返回值。

在实践中,一般使用void来表示“任何我不关心元素”,或者更常见的是,表示“省略”,例如在Future <void>或Stream <void>中。

get & set

在Java中,getter和setter应该是蛮令我们头疼,如果一些类的属性足够多的话,提供getter和setter接口后,文件很轻松可以达到成千上百行。但是在Dart中,提供了set和get关键子来提供对象属性的读写访问权限。

class Rectangle {
  num left, top, width, height;

  Rectangle(this.left, this.top, this.width, this.height);

  //定义两个可计算的属性right 和bottom.
  num get right => left + width;
  set right(num value) => left = value - width;

  num get bottom => top + height;
  set bottom(num value) => top = value - height;
}

void main() {
  var rect = Rectangle(3, 4, 20, 15);
  
  print('right: ${rect.right}');
  rect.right = 100;
  print('right: ${rect.right}');

  print('bottom: ${rect.bottom}');
  rect.bottom = 120;
  print('bottom: ${rect.bottom}');
}

//打印:
right: 23
right: 100
bottom: 19
bottom: 120

在Dart中,使用get和set实现getter和setter功能,是不是简洁多了呢

使用get和set后,我们可以从实例变量开始,在get/set的方法中用方法封装,而无需更改外部调用的代码。

while & do while

while: 在循环执行之前计算条件

while (!isDone()) {
  doSomething();
}

do-while: 在循环开始执行后计算条件

do {
  printLine();
} while (!atEndOfPage());
deferred

deferred用于声明一个延迟加载一个库,通常叫懒加载。允许你只有在需要使用该库的时候,再加载该库。

//文件calculate.dart
class Calculate{

  static String name = "Cal";

  static printf(String s){
    print('cal: $s');
  }

  int add(int a, int b) => a + b;
}

//文件test.dart
import 'calculate.dart' deferred as cal; //声明该库会延迟加载且命名为cal

void main() {
  print('1');
  greet();
  print('2');
  print('3');
}

//异步加载库calculate.dart
//加载完毕后再进行操作
Future greet() async {
  await cal.loadLibrary();
  print('cal name: ${cal.Calculate.name}');
  print('add(1, 2) = ${new cal.Calculate().add(1, 2)}');
  cal.Calculate.printf('ss');
}

//打印:
1
2
3
cal name: Cal
add(1, 2) = 3
cal: ss
return

用于在函数中返回一个值

推荐阅读更多精彩内容