Dart语法基础

Dart语言学习需要明确的重要概念:

  • Dart语言,一切实例皆对象,所有对象都继承自object。即便数字,函数和null也不例外。
  • Dart是强类型语言,支持类型推断,故类型注释是可选的。明确不需要类型推断时,可以使用dynamic关键字。

Dart中的两种类型允许所有值:objectdynamic
使用object类型注释,而不用dynamic,代表变量允许any object
使用dynamic发送更复杂的信号。这可能意味着Dart的类型系统尚不足够复杂和精准,无法表示允许的类型集。或明确希望在运行时变量具有动态性时可以使用dynamic

  • Dart支持泛型,如List,List
  • Dart支持顶级函数如:main();以及与类或对象绑定的静态方法和实例方法;支持嵌套或局部函数。
  • Dart支持顶级变量以及与类或对象关联的静态变量和实例变量。
  • 与Java不同,Dart没有关键字publicprotectedprivate。如果变量标识符以下划线_开头,则该标识符对于库来说便是私有的。

变量的声明:

正确:
var name = 'Bob'; name存储引用。
dynamic name = 'Bob';
String name = 'Bob';
错误:
var String name = "你好"//变量不能同时使用var 和 类型名称进行声明。

finalconst

final修饰的变量只会被设置一次,final修饰的顶级变量和类变量在首次使用时才会被初始化,即final修饰的变量运行时初始化。const修饰的变量是编译时常量(属于隐式的final变量)

注意:实例变量可以是final变量,但不能是const

字符串:

Dart中的字符串是UTF-16码元的序列。可以使用单引号或双引号来创建。

  1. 字符串的表示:
'...'或者"..."
  1. 字符串插值:
    插入表达式,使用{}:${ expression };插入变量$variable
var s = "123";
var s1 = "你好" + "${567}";//你好567
var s2 = "再见$s";//再见123
var s3 = "$s${789}";//123789
  1. 多行字符串
使用`'''`书写
 var s4 = '''这是
多行字符串,
多行书写即可。
''';
使用转义符`\n`
var s4 = '这是多行字符串,\n 多行书写即可';
  1. 原始字符串前缀前书写r,会使特殊字符不生效:
var s4 = r'这是多行字符串,\n 多行书写即可';
//log: '这是多行字符串,\n 多行书写即可'

数组:

  1. 创建可变数组
var mutableList = [4,5,6];
mutableList[0] = 99;
mutableList.add(88);
mutableList.insert(0, 77);//[77, 99, 5, 6, 88]
  1. 创建编译时常量数组
var constantList = const [1, 2, 3];
 constantList[1] = 10;//会触发错误
var constantList = const [1, 2, 3];
constantList = [3,4,5];//经此一步,可使用为可变数组
constantList[1] = 10;//[3, 10, 5]

3.创建不可变数组

const constantList =  [1, 2, 3];
//constantList = [3,4,5];//`constant`本身不允许
//constantList[1] = 10;//不允许会触发错误

4.Dart 2.3中引入了展开运算符(spread operator):....和空感知展开运算符(null-aware spread operator):...?

//使用`...`,将数组中的所有元素插入到另一个数组中.
var list = [2,3,4];
var list1 = [1,...list,9];//[1, 2, 3, 4, 9]
//使用`...?`避免插入null时异常
var list;//未赋值,默认为`null`
var list1 = [1,...?list,9];//[1, 9]

5.Dart 2.3 提供了collection ifcollection for, 允许我们使用if条件和for循环构建集合。
collection if使用

var show = true;
var list = [3,4,100,if(show) 300, 0];//[3, 4, 100, 300, 0]

collection for使用

var temp = [1,2,3];
var result = ["@1",for( var i in temp)  "@${i + 2}" ,"@3"];//[@1, @3, @4, @5, @3]

Set

  1. Dart中的Set
var set = {"cat",'dog','cow'};//1
//注意使用`var para = {}`创建的是`map`
var set1 = {};//2
Set set2 = {};//3
set1.add("动物园");
set1.addAll(set);//{动物园, cat, dog, cow}
  1. 创建编译时是常量的Set
final set2 = const {1,2,3,4};//常量

3.finalvar变量对比

var set2 = {1,2,3,4};
set2 = {2};
set2.add(5);
final set2 = {1,2,3,4};
set2 = {2};//报错,final只允许被设置一次。
set2.add(5);//不会报错。
var set2 = const {1,2,3,4};
//set2 = {2};//经此一步,才能操作set2,否则不能。
//set2.add(5);
  1. Set也支持......?运算符(Dart 2.3)
var set2 = {"dd"};
set2.add("cc");
var set1 = {"ggg",...set2,""};//{ggg, dd, cc, }
  1. Set支持collection ifcollection for(Dart 2.3)和list的做法一样。

Maps

1.Dart中的Map

var map = {}//默认创建为`Map`与`var map = Map()`一致;.
Map map2 = {};
map2[2] = "你好";//添加键值对
var map3 = {
      "str" : 9,
      3 : "number"
 };
var gifts = {
  // Key:    Value
  'first': 'partridge',
  'second': 'turtledoves',
  'fifth': 'golden rings'
};
  1. Map也支持...和...?运算符(Dart 2.3)
var map = {1: "你",2: "好"};
var map1 = {3 : "2020",...map,4:"come on"};//{3: 2020, 1: 你, 2: 好, 4: come}
  1. Map支持collection ifcollection for(Dart 2.3)
var show = true;
var map = {1: "你",2: "好", if(show) 6 : "dart"};

var map2 = { 8: "collection",for( var i in [4,3] ) i+1 : "value_$i" };
//{8: collection, 5: value_4, 4: value_3}

关于Unicode字符集的表示

Dart中字符串使用UTF-16码元的序列表示,因此表示Unicode时使用特定的语法:

若Unicode标量(16进制数)的位数等于4位时:\uXXXX;若大于或少于4位时:\u{标量值}

函数

Dart函数的示例

String _dartFunction(String para1, int para2) {
   return  para1 + "$para2";
}

建议公共API使用类型注释,但是省略也依然是有效的

  _dartFunction( para1, para2) {
    return para1 + "$para2";
  }

对于仅包含一个表达式的函数,可以使用简写语法:
=> expresion(箭头语法),它是{ return expresion }的简写语法

String _dartFunction( String para1, int para2) => para1 + "$para2";

注意:在简写语法中=>;之间只能出现一个表达式,而不是一个语句。例如,不可使用if语句,但是可以使用条件表达式。

以上三种方法示例,在函数调用时,不会有参数标签,并且参数不可省略。否则会提示:Error: Too few positional arguments: 2 required, 0 given. Dart中函数的参数被称为位置参数。

可选参数
Dart函数定义可选参数,有两种方式:

  • 通过{ param1, param2, … }实现参数命名(即:设置参数标签)。
//Case1 :参数全部可选
String _dartFunction({String para1, int para2}) {
     String temp = '';
     if (para1 != null) {
         temp += para1;
     }
     if (para2 != null) {
       temp += "$para2";
     }
     if (para1 == null && para2 == null) {
       temp = "默认值";
     }
     return temp;
}
//调用
_dartFunction()
_dartFunction(para1 : "你好", para2: 647)
_dartFunction(para1 : "你好")
_dartFunction(para2 : 647)
_dartFunction(para2 : 647, para1: "你好")
//Case 2 : 参数2可选
String _dartFunction(String para1,{ int para2}) {
  ......
}
//Case 2.1 : 使用Case1的方式实现Case 2的效果
String _dartFunction({ @required String para1,  int para2}) {
/*文档说`@required`可以强制函数提供此参数,否则报错,然而_dartFunction()并没有报错。*/
.... 
}
  • 通过[ param1, param2, … ]实现可选的位置参数,不会有参数标签。
//Case1 参数全部可选
String _dartFunction([String para1, int para2]) {
     ......
}
//调用
_dartFunction()
_dartFunction("你好")
_dartFunction("你好",1)
_dartFunction(1)//不允许 实测`一个参数时匹配参数1`
//Case2:参数2可选
String _dartFunction(String para1, [int para2]) {
     ......
}
//调用
_dartFunction("你好")
_dartFunction("你好",1)
_dartFunction()//不允许

使用[ ]实现可选参数,Dart语法不允许的方式(文档未描述)。

// Case 3
String _dartFunction({String para1}, int para2) {
......
}
String _dartFunction([String para1], int para2) {
......
}
//编译报错:`'para2' isn't defined`,感觉像语法缺陷。

使用= 为可选参数提供初始值,旧版本可能使用:提供初始值。

String _dartFunction({String para1 = "hello",  int para2 = 112}) {....}
String _dartFunction([String para1 = "hello",  int para2 = 112]) {....}
函数实例

可以将一个函数作为参数传递给另一个函数,也可以将函数分配给变量。

//case 1
var printElement = (int item) {
  print(item);
};
//case 2
void printElement(int element) {
  print(element);
}
var list = [1, 2, 3];
list.forEach(printElement);

匿名函数也称闭包:

([[Type] param1[, …]]) {
  codeBlock;
};

示例

var list = ['apples', 'bananas', 'oranges'];
list.forEach((item)=>print(item));//Case 1
list.forEach((item){
   print(item);
});//Case 2

函数支持嵌套。所有函数都返回一个值。如果未指定返回值,默认return null

操作符

描述 符号
一元后缀 expr++ , expr-- , () [] . ?.
一元前缀 -expr , !expr ,~expr , ++expr, --expr , await expr
算术运算符 * , / , %, ~/(整除,向下取整) , + , -
位运算符 <<(左移) , >>(右移), >>>,&(按位与),^(按位异或), |(按位或)
关系与类型运算符 >= , >, <=, <, as(type cast), is(if type return true) , is!(if type return false)
相等运算符 ==,!=
逻辑与 &&
逻辑或 ||
判空 ??
三目运算符 expr1 ? expr2 :expr3
级联 ..
赋值(含复合)运算符 =, *= , /=, +=, -= , &=,^=

使用注意:

as当且仅当我们确定该对象属于该类型时,才使用运算符将其转换为特定类型。在不确定时,需要先使用is运算符进行检查。

(emp as Person).firstName = 'Bob';

expr1 ?? expr2expr1 有值时便返回,否则返回expr2
a ??= b: 如果anull,则为a分配值b;否则,a保持不变。

级联:..
严格来说级联..并不是运算符,它只是Dart中的语法。允许我们对同一对象执行一系列操作,包括函数调用,访问同一对象上的字段。使用级联可以不用创建临时变量,更加快速和流畅。

class Person {
  String name;
  int age;

  personDescription() {
    print((name ?? "匿名者") + "年芳${age ?? 3}岁");
  }

  String personWork(){
    age ??= -1;
    if (age < 0){
      return "未知";
    } else if (age < 23&&age>0) {
      return "学习";
    }  else {
      return "工作";
    }
  }
}
final person = Person();
person
   ..personDescription() //匿名者年芳3岁
   ..personWork()
   ..age = 18
   ..name = "QiShare"
   ..personWork()//注意:需小心使用级联处理返回对象的函数。比如此处返回值并没有被用到。
   ..personDescription(); //QiShare年芳18岁
//等价于
final person = Person();
person.personDescription(); //匿名者年芳3岁
person.personWork();
person.age = 18;
person.name = "QiShare";
person.personWork();
person.personDescription(); //QiShare年芳18岁

控制流

  • if and else
  • for loops
  • while and do-whileloops
  • break and continue
  • switch and case 属于自动贯穿,需要break
    case会自动贯穿。
var command = 'CLOSED';
switch (command) {
  case 'CLOSED': // Empty case falls through.
  case 'NOW_CLOSED':
    // Runs for both CLOSED and NOW_CLOSED.
    executeNowClosed();
    break;
}

如果需要贯穿可以使用continue标签

var command = 'CLOSED';
switch (command) {
  case 'CLOSED':
    executeClosed();
    continue nowClosed;
  // Continues executing at the nowClosed label.

  nowClosed:
  case 'NOW_CLOSED':
    // Runs for both CLOSED and NOW_CLOSED.
    executeNowClosed();
    break;
}
  • assert
    开发代码时可以帮助我们调试代码,在生产代码中,断言会被忽略,并且assert不评估to的参数。
assert(text != null);

异常

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

Throw
抛出异常

throw FormatException('抛出异常');
throw "抛出异常";

注意:为了代码质量,通常会抛出ExceptionError 类型的异常。
抛出异常是一个表达式,所以可以在=>语句以及允许表达式的任何其他地方抛出异常。

Catch
捕获异常会阻止该异常的传播(除非您重新抛出该异常)
要处理可能引发不止一种类型的异常的代码,可以指定多个catch子句。使用on指定异常类型,匹配抛出异常的类型,并进行处理;未指定时,处理任何类型的异常。

 try {
      throw FormatException("一个小异常");//1
      throw "字符串异常";//2
    } on Exception catch(e) {
      print("捕获了一个异常${e.runtimeType}" );//1
    } catch (e) {
      print("捕获了一个异常${e.runtimeType}" );//2。
}

若需要允许捕获的异常重新传播,需要使用rethrow关键字。只允许使用在catch闭包中。
finally
为了确保某些代码无论是否引发异常都可以运行,请使用finally子句。如果没有catch子句与该异常匹配,则在该finally子句运行后传播该异常

1.不添加catch,使用finally,不会阻止异常的传播。

try {
      throw FormatException("一个小异常");//1
      throw "字符串异常";//2
    } finally {
      print("执行finally");
}

2.添加catch,阻止异常传播;finally子句会在所有catch处理完成,再执行。

try {
      throw FormatException("一个小异常");//1
      throw "字符串异常";//2
    } on Exception catch(e,s) {
      print("捕获了一个异常${e.runtimeType}" );//1
      print("捕获异常时,函数的调用堆栈+${s}");
    } catch (e) {
      print("捕获了一个异常${e.runtimeType}" );//2
    } finally {
      print("执行finally");
    }

Class

Dart是一种具有类和基于Mixin继承的面向对象语言。
构造函数
1.通过创建一个与其类具有相同名称的函数来声明一个构造函数。这是构造函数最常见的形式。

class Point {
  num x,y;
  Point(num x,num y) {
    this.x = x;
    this.y = y;
  }
  String toString() {
    return "(${this.x},$y)";
  }
}

this 引用当前对象。需要注意的是:只有当命名冲突的时候,才会使用this,一般Dart的风格都是忽略它。
将构造函数参数分配给实例变量的模式非常普遍,Dart使用语法糖使其变得简单:

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

2.默认构造函数:如果不声明构造函数,则会提供默认的构造函数。默认构造函数没有参数,并在超类中调用无参数构造函数。(初始化的顺序,决定)
3.构造函数不会被继承:
子类不从其父类继承构造函数。声明没有构造函数的子类仅具有默认构造函数。
4.命名构造函数:使用命名构造函数可为一个类实现多个构造函数或提供额外的描述信息:

class Point {
  num x,y;
  Point(this.x,this.y);
  //命名构造函数
  Point.init(this.x,this.y);
  Point.origin(x,y){
    this.x = x;
    this.y = y;
  }
  String toString() {
    return "(${this.x},$y)";
  }
}

记住,构造函数没有继承,这意味着子类不会继承超类的命名构造函数。如果要使用超类中定义的命名构造函数创建子类,则必须在子类中实现该构造函数。

4.子类调用父类非默认(无参,无名)构造函数:
默认情况下,子类中的构造函数会调用父类的未命名,无参数的构造函数。父类的构造函数在构造函数主体的开头被调用。如果还使用了初始化参数列表,它将在调用父类之前执行。
执行顺序如下:
a.初始化主类参数
b.父类的无参构造函数
c.主类的无参构造函数

如果父类没有未命名,无参数的构造函数,则必须在构造函数中手动调用父类的某个构造函数。
调用示例一:
父类不存在无参,未命名的构造函数时,子类不管是重新实现父类的构造函数,还是命名的构造函数,都必须调用父类的构造函数。

class Point {
  num x,y;
  Point(this.x,this.y);
  Point.init(this.x,this.y );
  Point.origin(x,y){
    this.x = x;
    this.y = y;
  }
}
class SubPoint extends Point {
  SubPoint.init(num x , num y) : super.origin(x,y);
  SubPoint.subInit(x,y):super.init(x,y);
}

因为,父类的构造函数,在子类的构造函数之前被调用,故还可以写为:

class SubPoint extends Point {
  SubPoint.init() : super.origin(2,6);
  SubPoint.subInit(x,y):super.init(x,y+5);
}

调用示例二:
父类存在无参,未命名的构造函数时,可以省略调用父类的构造函数。

class Point {
  num x,y;
  Point({this.x,this.y});//参数省略

}
class SubPoint extends Point {
  num z;
  SubPoint.init(num x , num y) : super();//可以省略
  SubPoint.subInit(this.z,x,y);
}

初始化实例变量:
可以在构造函数体之前初始化实例变量。

class Point {
  num x,y;
//注意:this.x = *,this.y =* ,此处的*不能使用`this`属性。
  Point.origin(x,y):this.x = x,this.y = y{
    print('In Point.origin(): ($x, $y)');
  }
  Point.fromJson(Map json) : x = json['x'],
        y = json['y'] {
    print('In Point.fromJson(): ($x, $y)');
  }
}
class Point {
  final num x;
  final num y;
  final num distanceFromOrigin;

  Point(x, y)
      : x = x,
        y = y,
        distanceFromOrigin = sqrt(x * x + y * y);
}

重定向构造函数
类似便利初始化方法。

class Point {
  num x, y;

  // The main constructor for this class.
  Point(this.x, this.y);

  // Delegates to the main constructor.
  Point.alongXAxis(num x) : this(x, 0);
}

常量构造函数
如果类可以产生永不改变的对象,则可以让这些对象是编译时常量。为此,我们可以定义一个const构造函数,并确保所有实例变量均为final

class ImmutablePoint {
  static final ImmutablePoint origin =
      const ImmutablePoint(0, 0);
  final num x, y;
  const ImmutablePoint(this.x, this.y);
}

Factory 构造函数
当类实现一个并不总是创建当前类对象的构造函数时,需要使用factory关键字。比如:factory构造函数可能从缓存中返回一个实例,或从它的子类中返回一个实例。
需要注意的是Factory 构造函数,没有使用this的权限。

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

  // _cache is library-private, thanks to
  // the _ in front of its name.
  static final Map _cache =
      {};

  factory Logger(String name) {
    return _cache.putIfAbsent(
        name, () => Logger._internal(name));
  }

  Logger._internal(this.name);

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

获取对象的类型
要在运行时获取对象的类型,可以使用ObjectruntimeType属性,该属性返回Type对象。
实例变量

class Point {
  num x; // Declare instance variable x, initially null.
  num y; // Declare y, initially null.
  num z = 0; // Declare z, initially 0.
}

所有未初始化的实例变量都具有值null
所有实例变量都会生成一个隐式的getter方法。非final实例变量也会生成隐式的setter方法。

方法

1.定义实例方法

class Point {
  num x, y;

  Point(this.x, this.y);

  num distanceTo(Point other) {
    var dx = x - other.x;
    var dy = y - other.y;
    return sqrt(dx * dx + dy * dy);
  }
}

2.Gettersetter
可提供对对象属性的读写。
每个实例变量都有一个隐式的getter,如果合适的话还有一个setter;可以使用getset关键字通过实现gettersetter来创建其他属性。

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

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

  // Define two calculated properties: right and 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);
  assert(rect.left == 3);
  rect.right = 12;
  assert(rect.left == -8);
}

3.定义抽象方法
实例方法,gettersetter方法可以是抽象的,定义一个接口,但将其实现留给其他类实现。抽象方法只能存在于抽象类中。
定义抽象方法:使用;代替方法体即可。

abstract class Doer {
  // Define instance variables and methods...

  void doSomething(); // Define an abstract method.
}

class EffectiveDoer extends Doer {
  void doSomething() {
    // Provide an implementation, so the method is not abstract here...
  }
}

抽象类

抽象类,一个无法被实例化的类。
使用abstract修饰符定义一个抽象类。抽象类通常用于定义接口(有时也定义接口的实现)。如果希望抽象类可实例化,可以定义一个工厂构造函数(可返回子类的实例)。

// This class is declared abstract and thus
// can't be instantiated.
abstract class AbstractContainer {
  // Define constructors, fields, methods...
  void updateChildren(); // Abstract method.
}
隐式接口

每个类都会隐式定义一个接口,这个接口包含所有实例变量,以及除构造函数外的所有方法接口。
如果要创建一个支持类B接口的类A,但是却不继承类B的实现。类A需要实现类B的接口。
类A实现一个或多个接口,通过在implements子句声明它们,然后类A提供类B所有接口的实现。

// A person. The implicit interface contains greet().
class Person {
  // In the interface, but visible only in this library.
  final _name;
  // Not in the interface, since this is a constructor.
  Person(this._name);
  // In the interface.
  String greet(String who) => 'Hello, $who. I am $_name.';
}
// An implementation of the Person interface.
class Impostor implements Person {
  get _name => '';

  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')));
  print(greetBob(Impostor()));
}

这是指定一个类实现多个接口的示例:

class Point implements Comparable, Location {...}

通过implements指定一个类实现其他类隐式接口的方式,有点像Swift中的协议(存在类型)。协议的声明—>隐式接口,协议的实现—>通过implements约束其他类实现。这种方式让类本身抽象成了协议的定义。举个例子:

class Person {
  String name;
  int age;
}

class Student extends Person {
  learn(){
    print("学习");
  }
}
class Teacher extends Person {
  teach(){
    print("教书");
  }
}

class PartTimeStudent implements Teacher, Student {
  String name = "";
  int age = 12;
  learn() {
    print("$name兼职身份:学习");
  }
  teach(){
    print("$name兼职身份:教书");
  }
}
//定义调用的方法:
  greet(Person person){
    if (person is Teacher){
      person.teach();
    }
    if (person is Student) {
      person.learn();
    }
    if (person is PartTimeStudent) {
      person.learn();
      person.teach();
    }
}
//调用
greet(PartTimeStudent()..name = "Qishare");
//执行结果:
Qishare兼职身份:教书//`person is Teacher`判定生效
Qishare兼职身份:学习//`person is Student`判定生效
//`person is PartTimeStudent`判定生效。
Qishare兼职身份:学习 
Qishare兼职身份:教书

根据上例总结:

  1. 通过这种方式可实现多继承。

2.PartTimeStudent的实例,是实现了Person,Teacher,Student接口的实例。而(Person person)允许实现了Person接口的类型传入。后续类型判断采用接口类型,只要是实现了此接口的类型都可传入。接口类型:Swift协议类型 。

类继承
  1. 关键字extends创建子类 :class Student extends Person
  2. 关键字super引用父类。
  3. 子类可以重写父类的gettersetter以及实例方法。子类使用@override标记子类将要进行重写。
class Person {
  String name;
  int age;
  work() {
    print("person工作内容");
  }
}

class Student extends Person {
  @override
  work(){
    //super.work();
    print("student工作内容:学习");
  }
}
  1. 可以重写的操作符


    可以重写的操作符.png

!= 不能被重写,因为它是!(*==*)的语法糖。如果重写==,还必须重写ObjecthashCodegetter方法。

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);
  // ···
}
final v = Vector(2, 3);
final w = Vector(2, 2);
assert(v + w == Vector(4, 5));
assert(v - w == Vector(0, 1));
  1. noSuchMethod()

若代码使用了不存在的实例方法或者实例变量时,我们需要检测并作出反应,如消息转发,我们可以重写noSuchMethod()

class A {
  // Unless you override noSuchMethod, using a
  // non-existent member results in a NoSuchMethodError.
  @override
  void noSuchMethod(Invocation invocation) {
    print('You tried to use a non-existent member: ' +
        '${invocation.memberName}');
  }
}

方法扩展Extension

Extension methods:Dart 2.7中引入,是一种向现有类库添加功能的方式。
使用场景:当使用其他人的API或广泛使用的库时,更改API通常是不切实际或不可能的。但是可能仍想添加一些功能。
扩展不仅可以定义方法,还可以定义其他成员变量,例如gettersetteroperator
1.创建扩展语法:

extension  on  {
  ()*
}

2.使用示例:

class Person  {
  String name;
  int age;

}
extension Persion_extension on Person {
  String get completedName => "我叫$name";
  set realAge(newValue) {
    print(newValue);
  }
  String introduce()=> "$completedName,今年$age岁";
}
//使用
Person person = Person()..name = "Qishare"..age = 2;
print(person.completedName);//我叫Qishare
print(person.introduce());//我叫Qishare,今年2岁

枚举类型

枚举类型是一种特殊的类,用于表示固定数量的常量值。
使用enum关键字声明枚举类型。

enum Color { red, green, blue }

枚举的每个值都有indexgetter方法。返回枚举声明从零开始的位置。

assert(Color.red.index == 0);
assert(Color.green.index == 1);
assert(Color.blue.index == 2);

要获取枚举中所有值的列表,请使用枚举的values常量。

List colors = Color.values;
assert(colors[2] == Color.blue);

枚举类型具有以下限制:
1.不能显式实例化一个枚举类型。
2.不能子类化,mixInimplement一个枚举类型。

向类添加功能:mixin

mixin:是一种在多个类的层次结构中复用类代码的一种方式。
声明一个mixin的类与普通的类基本一致,唯一的区别,使用mixin声明的类,没有构造方法。声明mixin类的时候,有时会使用on关键字,限制mixin类只能被某些特定的类型使用,on可以指定使用类的父类。
需要混入代码的类使用with关键字指定一个或多个mixin的类。

class Person  {
  String name;
  int age;

}
//限制使用类为`Person `的子类
mixin MixinClaseName on Person {
  String accessPersonName()=> "我叫$name";
}
//使用此`mixin`类
class Student  extends Person with MixinClaseName {
}
//调用
Student sdu = Student()..name = "wally"..age = 12;
print(sdu.accessPersonName());

类变量和方法

使用static关键字修饰来实现类变量和方法。

泛型

泛型通常是类型安全所必需的,使用泛型可以减少代码重复。
使用示例:

class SomeBaseClass {...}

class Foo {
  void printSomeMsg(S param1,T param2) {
    print("Instance of 'Foo<$param1>'_'Foo<$param2>'");
  }
  String toString() => "Instance of 'Foo<$T>'";
}

class Extender extends SomeBaseClass {...}

库的使用

import指定库的URI,对于内置库,URI具有特殊的dart:schemes,对于其他库,可以使用文件系统路径或package: shemes(指定由包管理器(例如pub工具)提供的库)。

指定库前缀

如果导入两个标识符冲突的库,则可以为一个或两个库指定一个前缀。例如,如果library1和library2都具有Element类,那么可能具有以下代码:

import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;

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

// Uses Element from lib2.
lib2.Element element2 = lib2.Element();
延时使用

延迟加载(也称为延迟加载)允许Web应用程序在需要库时按需加载库。减少Web应用程序的初始启动时间。

仅dart2js支持延迟加载。 Flutter,Dart VM和dartdevc不支持延迟加载。

要延迟加载库,首先导入它并使用deferred as

import 'package:greetings/hello.dart' deferred as hello;

使用时,请loadLibrary()使用库的标识符进行调用 。

Future greet() async {
  await hello.loadLibrary();
  hello.printGreeting();
}

异步支持

Dart库充满了返回FutureStream对象的函数。这些函数是异步的:它们的返回值可能在耗时操作(例如I / O)之后返回,而无需等待该操作完成。
asyncawait关键字支持异步编程,让我们可以编写看起来类似于同步代码的异步代码。

处理Future

当需要Future执行完成的结果时,有两种选择:

  • 使用asyncawait
  • 使用Future API。

使用asyncawait的代码是异步的,但是看起来很像同步代码。

await lookUpVersion();

要使用await,代码必须在async标记的函数中。

Future checkVersion() async {
  var version = await lookUpVersion();
}

即使async的函数可能执行耗时的操作,但是它不会等待这些操作。异步函数仅在遇到第一个await表达式(details)时,才会执行。仅在await表达式完成后,返回Future对象,才会恢复执行。

使用 try, catch, 和finally去处理用了await代码的错误情况 。

try {
  version = await lookUpVersion();
} catch (e) {
  // React to inability to look up the version
}

在一个async异步函数中可以多次使用await

asyncFunc() async {
  var entrypoint = await findEntrypoint();
  var exitCode = await runExecutable(entrypoint, args);
  await flushThenExit(exitCode);
}

await expressionexpression的值通常是一个Future对象。如果不是,也会自动包装成Future对象。Future对象意味着一个返回对象的承诺[promise]await expression的值,便是那个返回的对象。此表达式使执行暂停,直到其返回的对象可用为止。

注意:当使用await的时候,出现编译错误,请确保await出现在async函数中。

异步函数的声明

异步函数,是一个函数体被async关键字修饰的函数。
为一个函数添加async关键字,可以使其返回一个Future对象。
下例为同步函数与异步函数的使用示例,可以清楚的看到两者的区别:

//同步函数
String lookUpVersion() => '1.0.0';
//异步函数:返回值是,Future
Future  lookUpVersion()  async =>'1.0.0';

需要注意的是函数体不需要使用Future的API,Dart会创建所需的Future对象。如果函数不会返回有用的结果,可以让返回值为Future

处理Streams

当我们需要从一个Stream中获取值时,我们有两种选择:

  • 使用async 和 一个异步的for循环( await for)。
  • 使用Stream API, 详细 描述。

注意:使用await for之前, 请确保代码清晰并且我们真正需要等待所有Stream的结果。比如,通常不应将await for用于UI事件侦听器,因为UI框架发送无休止的事件Stream

一个异步的for循环的格式:

await for (varOrType identifier in expression) {
 // Executes each time the stream emits a value.
}

expression的值必须具有Stream类型。执行过程如下:

  1. 等到Stream发出一个值。
  2. 执行for循环的方法体,并使用发出的值为变量赋值
  3. 重复12直到Stream关闭。

要停止监听流,可以使用breakreturn语句,该语句会跳出for循环并取消对流的监听。

生成器(Generators

当需要延迟生成值的序列时,可以使用生成器函数。Dart内置支持两种生成器:

  1. Synchronous生成器:返回Iterable对象
  2. Asynchronous生成器:返回Stream对象
    实现一个Synchronous生成器, 使用sync*标记函数体,使用yield语句传递值。
Iterable courseList(int n) sync* {
    int k = 0;
    while (k < n) yield "课程${k++}";
  }
print(person.courseList(8));//(课程0, 课程1, 课程2, 课程3, 课程4, 课程5, 课程6, 课程7)
for (var i in person.courseList(8)){
    print(i);//课程i
}

实现一个Asynchronous生成器, 使用async*标记函数体,使用yield语句传递值。

Stream courseList(int n) async* {
    int k = 0;
    while (k < n) yield "课程${k++}";
  }

Future> getAsyncList(int n) async {
    final list = [];
    await for (var i in courseList(n)) {
      list.add(i);
    }
    return list;
}

person.getAsyncList(8).then((res){
      print(res);//[课程0, 课程1, 课程2, 课程3, 课程4, 课程5, 课程6, 课程7]
});

可调用的类

实现call()方法可以让Dart类能向函数一样被调用。

class WannabeFunction {
  String call(String a, String b, String c) => '$a $b $c!';
}

var wf = WannabeFunction();
var out = wf('Hi', 'there,', 'gang');

隔离(Isolates

许多计算机,甚至移动平台,都有多核的CPU,为了利用多核CPU,开发人员传统上使用可共享内存的线程,并发运行。但是,并发的共享状态易于出错,并且可能导致复杂的代码。
所有Dart代码都在隔离体isolates内运行,而不是线程。每个隔离(isolate)区都有自己的内存堆,从而确保任何其他隔离区都无法访问隔离区的状态。
隔离区和事件循环详情

Typedef

在Dart中,函数是对象,就像字符串和数字是对象一样。typedef为类型提供别名

typedef Compare = int Function(Object a, Object b);

class SortedCollection {
  Compare compare;

  SortedCollection(this.compare);
}

注意:当前,typedef仅限于函数类型。

MetaData

使用元数据提供有关代码的其他信息。元数据的批注以字符@开头,后跟对编译时常量的引用(例如:deprecated)或对常量构造函数的调用。
所有Dart代码都有两个注释:@deprecated@override
使用@deprecated注释的示例:

class Television {
  /// _Deprecated: Use [turnOn] instead._
  @deprecated
  void activate() {
    turnOn();
  }

  /// Turns the TV's power on.
  void turnOn() {...}
}

可以定义自己的元数据注释。这是定义带有两个参数的@todo注释的示例:

library todo;

class Todo {
  final String who;
  final String what;

  const Todo(this.who, this.what);
}

使用

import 'todo.dart';

@Todo('seth', 'make this do something')
void doSomething() {
  print('do something');
}

参考资料:
Dart-Language-Tour

你可能感兴趣的:(Dart语法基础)