语言简介 Dart  语言是为客户端开发而设计,优先考虑多平台的开发,它的主要特点:
编译型:为了支持多平台而开发的各种编译器 强类型:使用静态类型检查,也支持 dynamic 类型及运行时类型检查 GC :由 Dart 运行时环境负责分配和管理内存 面向对象:基于 mixin 继承机制 Dart 平台 Dart 通过编译技术来支持各种不同的平台:
原生平台:对于移动和桌面平台,Dart 拥有具有实时编译功能(JIT)的 Dart VM ,和用于生成机器代码的 AOT 编译器。开发的时候,Dart VM 通过 JIT 提供增量重编译、热重载、运行时数据收集及各种调试能力。部署到生产环境的时候通过 AOT 编译器将代码编译成对应平台的机器码。 Web 平台:针对 Web 应用程序,Dart 有开发时编译器(dartdevc)和生产时编译器(dart2js),两者都可以将 dart 代码编译成 js 代码。 Hello World Copy 1 
2 
3 
4 
// hello.dart
 void  main() {
  print("Hello World!" );
 } 
通过 dart run hello.dart 就可以运行上面的代码。
在 Windows 上,我们可以通过 dart compile exe hello.dart 将程序编译成 exe 文件。
编译后生成的是可执行的 hello.exe,我们用 dumpbin.exe /dependents hello.exe
查看它的依赖可以发现它只依赖操作系统的库,其它的全部编译到了 exe 里:
Copy  1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
 Image has the following dependencies:
 
   IPHLPAPI.DLL
   PSAPI.DLL
   WS2_32.dll
   RPCRT4.dll
   SHLWAPI.dll
   ADVAPI32.dll
   SHELL32.dll
   dbghelp.dll
   bcrypt.dll
   CRYPT32.dll
   KERNEL32.dll 
我们可以用 dart compile js hello.dart 将 dart 编译成 js 代码,用 node 执行生成的 js 文件可以得到同样的输出,把生成的代码放到浏览器里也是可以执行的。
变量 和大多数 GC 语言一样,Dart 中的变量都只是引用到一个对象,每个对象都是一个类的实例。数字、函数以及 null 都是对象。除了 null 外,所有的类都继承于 Object  类。
Dart 的变量具有*词法作用域*
Dart 是强类型的,没有隐式类型转换,下面的代码会报错,无法从 double 转成 int 。
Copy 1 
int  number =  5.5 ; //  error A value of type 'double'  can't be assigned to a variable of type ' int '. 
只能用 double 类提供的 toInt 方法进行转换
Copy 1 
2 
int  number =  5.5 .toInt();
print(number); 
Dart 有自动类型推导,在声明变量时指定类型是可选的,下面的 number 自动推导成 int 类型。
Copy 1 
2 
var  number =  10 ;
print(number.runtimeType); 
Dart 支持声明接收任意类型的变量,在运行时动态改变。
Copy 1 
2 
3 
4 
Object  any =  "ANY" ;
print(any.runtimeType);
 any =  2 ;
 print(any.runtimeType); 
空安全 Dart 中的类型默认是非空的,除非声明它们可为空。
Copy 1 
int  number =  null ;//  Error:  The value 'null'  can't be assigned to a variable of type ' int ' because ' int ' is not nullable. 
要想声明一个变量可为空,就在类型声明后面加上 ?
Copy 1 
2 
int ?  number =  null ;
print(number); 
Dart 的空安全据说是完全可靠的,如果一个变量不可为空,那么它永远不为空。
默认值 对于不可空类型,在使用之前必须初始化
Copy 1 
2 
int  number;
print(number);//  Error:  Non- nullable variable 'number'  must be assigned before it can be used. 
Copy 1 
2 
3 
int  number;
number =  5 ;
 print(number); 
对于可空类型,不初始化默认为 null
Copy 1 
2 
int ?  number;
print(number); 
Late 变量 Dart 2.12 添加了 late 变量修饰符,它的作用有两个:
延迟初始化不可空变量 延迟初始化变量 有时 Dart 推导非空类型初始化时会失败,如:
Copy 1 
2 
3 
4 
5 
int  number;
void  main() {
  number =  10 ;
   print(number); // Error: Field 'number' should be initialized because its type 'int' doesn't allow null.
  
这个时候可以加上 late 修复这个问题
Copy 1 
2 
3 
4 
5 
late int  number;
 void  main() {
  number =  10 ;
   print(number);
 } 
当在声明变量同时初始化时,加上 late 可以延迟初始化,主要有两种场景用到:
这个变量可能不会被用到,或者初始化它花的时间比较长 需要初始化一个实例变量,但是在构造函数里需要用到 this Copy  1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
int  delayInit() {
  print("delay init" );
   return  10 ;
 }
 
 void  main() {
  late int  number =  delayInit();
   print("do some things" );
   print(number);
 } 
Copy 1 
2 
3 
do some things
 delay init
 10 
Final 和 Const final 修饰的变量只可以被赋值一次,=const= 修饰的变量是一个编译期常量,必须在编译期计算出来。
Copy 1 
2 
final  int  number =  5 ;
number =  10 ; //  Error:  Can't assign to the final variable ' number'.  
final 可以赋值一次
Copy 1 
2 
3 
4 
final  int ?  number;
number =  10 ;
 print(number); // 10
  =  20 ;//  Error:  Final variable 'number'  might already be assigned at this  point.
const 修饰的变量必须在编译期就确定
Copy 1 
2 
3 
4 
5 
6 
7 
8 
int  f() {
  return  10 ;
 }
 
 void  main() {
  const  int  number =  f(); // Error: Method invocation is not a constant expression.
  } 
右边的值必须在编译器确定
Copy 1 
2 
3 
4 
void  main() {
  const  int  number =  10 ;
   print(number);
 } 
const 也可以创建常量值,对应的变量值是可以改变的,即使它引用过 const 值
Copy 1 
2 
3 
4 
var  number =  const  [];
print(number);
 number =  [1 , 2 , 3 ];
 print(number); 
内置类型 Numbers Dart 支持两种内置类型:=int= 和 double ,如果一个数字包括了小数点,它就是 double ,否者就是 int
Copy 1 
2 
3 
4 
var  n =  1 ;
print(n.runtimeType);
 var  m =  1.1 ;
print(m.runtimeType); 
整型字面量可以自动转换成 double 类型,反之就不行。
Copy 1 
2 
double  m =  1 ;
print(m); 
int 和 double 的父类型是 num
Copy 1 
2 
3 
num  n =  1 ;
n +=  1.5 ;
 print(n); 
Number 和 String 之间的转换如下:
Copy  1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
// String => int
 var  one =  int .parse("1" );
print(one);
 // String => double
 var  onePointOne =  double .parse("1.1" );
print(onePointOne);
 // int => String
 1. toString());
// double => String
 3.14159 .toStringAsFixed(2 ));
Strings Dart 字符串是 UTF-16 编码的字符序列,可以使用单引号和双引号创建字符串,或者使用三引号创建多行字符串。
Copy 1 
2 
3 
4 
5 
6 
7 
8 
9 
var  s1 =  "first" ;
var  s2 =  'second' ;
var  s3 =  """first
 second
 """ ;
var  s4 =  '''first
 second
 ''' ;
print(s4); 
使用 ${表达式} 在字符串中插入表达式,如果表达式是一个标识符,可以省略 {} ,如果是一个对象,会自动调用对象的 toString 方法。当然,加上 \ 就可以当成原本的字符。
Copy 1 
2 
3 
String  s =  "Jack" ;
print("My name is  $ s" );
 print("My name is  \$ s" ); 
Copy 1 
2 
My name is Jack
 My name is $s 
字符串前面加 r 创建 raw 字符串
Copy 1 
2 
String  s =  r"This is \" ;
print(s); 
字符串操作 使用 contains startsWith endsWith indexOf 在字符串内搜索
Copy 1 
2 
3 
4 
5 
String  s =  "Hello, my name is Jack" ;
print(s.contains("name" ));
 print(s.startsWith("Hello" ));
 print(s.endsWith("Jack" ));
 print(s.indexOf("my" )); 
使用 substring 提取子串,=split= 分割字符串,=toUpperCase= toLowerCase 转换大小写,=trim= 移除首尾空格
Copy 1 
2 
3 
4 
5 
6 
String  s =  "Hello World" ;
print(s.substring(0 , 5 ));
 print(s.split(" " ));
 print(s.toUpperCase());
 print(s.toLowerCase());
 print("   hello   " .trim()); 
Copy 1 
2 
3 
4 
5 
Hello
 [Hello, World]
 HELLO WORLD
 hello world
 hello 
使用 replaceAll 替换部分字符串
Copy 1 
2 
String  s  =  "Hello World" ;
print(s.replaceAll(RegExp("World" ), "Jack" )); 
构造字符串可以使用 StringBuffer , writeAll 第二个可选参数为分割字符
Copy 1 
2 
3 
4 
5 
6 
var  sb =  StringBuffer();
sb
 ..write("Use StringBuffer" )
 ..writeAll(["A" , "B" , "C" ], "/" )
 ..write("!" );
 print(sb.toString()); 
更多的 API: String 
布尔类型 Dart 使用 bool 关键字表示布尔类型,布尔类型的只有 true 和 false 两个对象。
Dart 的类型安全不允许其它类型隐式转换成布尔类型,比如数字 0 在 if 里并不是 fasle 。
Lists Dart 中的 List  对象表示的就是数组类型,下标从 0 开始,可以用 [] 访问
Copy 1 
2 
3 
4 
var  list =  [1 , 2 , 3 ];
print(list.length);
 print(list[1 ]);
 print(list.runtimeType); 
上面的声明会被推导成 List<int> 类型
扩展操作符(=…=) 和空扩展操作符(=…?=)提供了一种将多个元素插入集合的简洁方法。
Copy 1 
2 
3 
4 
5 
6 
7 
var  list1 =  [1 , 2 , 3 ];
var  list2 =  [0 , ...list1];
var  list3 =  [...list1, 4 ];
var  list4 =  [0 , ...list1, 4 ];
print(list2);
 print(list3);
 print(list4); 
Copy 1 
2 
3 
[0, 1, 2, 3]
 [1, 2, 3, 4]
 [0, 1, 2, 3, 4] 
如果扩展操作符右边可能为 null ,就使用空扩展操作符避免异常。
Copy 1 
2 
3 
var  list;
var  list1 =  [1 , ...? list];
print(list1); 
Dart 还可以在构建集合时使用 if 或者 for 。
Copy 1 
2 
3 
4 
5 
6 
7 
8 
var  n =  3 ;
var  list =  [
  1 ,
   2 ,
   if  (n ==  0 ) 3 
   else  4 
 ];
 print(list); 
Copy 1 
2 
3 
var  list =  [1 , 2 , 3 ];
var  list1 =  [for  (var  i in  list) i];
print(list1); 
List 常用操作 add addAll 添加元素,=removeAt= 删除单个元素,=clear= 删除所有元素
Copy 1 
2 
3 
4 
5 
6 
7 
8 
var  list =  [1 ];
list.add(2 );
 list.addAll([3 , 4 ]);
 print(list);
 list.removeAt(0 );
 print(list);
 list.clear();
 print(list); 
Copy 1 
2 
3 
[1, 2, 3, 4]
 [2, 3, 4]
 [] 
indexOf 查找一个对象对应的下标值
Copy 1 
2 
var  list =  ["Apple" , "Orange" ];
print(list.indexOf("Orange" )); 
sort 排序
Copy 1 
2 
3 
var  list =  [3 , 1 , 2 , 5 , 4 ];
list.sort();
 print(list); 
Sets Dart 中的 Set 是无序元素的集合
Copy 1 
2 
3 
4 
5 
6 
var  set  =  {"Apple" , "Orange" };
print(set .runtimeType);
 var  set1 =  < String > {};
print(set1.runtimeType);
 Set< int >  set2 =  {};
 print(set2.runtimeType); 
Copy 1 
2 
3 
_CompactLinkedHashSet<String>
 _CompactLinkedHashSet<String>
 _CompactLinkedHashSet<int> 
Set 和 Map 的声明类似,如果声明时不指定元素,默认会推导成 Map
Copy 1 
2 
var  itIsMap =  {};
print(itIsMap.runtimeType); 
Copy 1 
_InternalLinkedHashMap<dynamic, dynamic> 
Set 同样支持 ... 和 ...? 操作符,以及集合内的 if 和 for
Set 常用操作 使用 from 从 List 构造 Set
Copy 1 
print(Set.from([1 , 2 , 3 ])); 
add addAll 添加元素,=remove= 移除一个元素,=clear= 移除所有元素
Copy 1 
2 
3 
4 
5 
6 
7 
8 
Set< int >  set  =  {};
 set .add(1 );
set .addAll([2 , 3 ]);
print(set );
 set .remove(2 );
print(set );
 set .clear();
print(set ); 
Copy 1 
2 
3 
{1, 2, 3}
 {1, 3}
 {} 
contains 和 containsAll 检查一个或多个元素是否在 Set 中
Copy 1 
2 
3 
var  set  =  {1 , 2 , 3 , 4 };
print(set .contains(3 ));
 print(set .containsAll([1 , 4 ])); 
intersection 求交集 ,
Copy 1 
2 
3 
4 
var  set1 =  {1 , 2 , 3 };
var  set2 =  {2 , 3 , 4 };
print(set1.intersection(set2));
 print(set1.difference(set2)); 
更多操作看相关的 API : https://api.dart.cn/stable/dart-core/Set-class.html 
Maps Map 就是 key-value 对象,key value 可以为任意类型
Copy 1 
2 
3 
4 
var  map =  {
  "first"  :  1 ,
   "second"  :  2 
 }; 
可以使用 [] 操作 Map
Copy 1 
2 
3 
4 
5 
6 
7 
var  map =  {
  "first"  :  1 
 };
 print(map["first" ]);
 map["second" ] =  2 ;
 print(map);
 print(map["third" ]); 
Copy 1 
2 
3 
1
 {first: 1, second: 2}
 null 
Map 同样支持 ... 和 ...? 操作符以及 if for
Map 的 API 直接看相关文档:Map api docs 
函数 Dart 的函数也是对象,它的类型是 Function  ,它是一级对象,可以复制给变量,也可以作为参数
Copy 1 
2 
3 
4 
5 
void  f() {
}
 void  main() {
  print(f.runtimeType);
 } 
Dart 的函数都有返回值,如果不写,默认返回 null ,定义时可以没有返回类型,但是推荐在公开的 API 上加上返回类型
Copy 1 
2 
3 
4 
5 
6 
7 
f() {
   return  10 ;
 }
 
 void  main() {
  print(f());
 } 
如果函数体只有一个表达式,可以使用 =>
Copy 1 
2 
f() =>   10 ;
 print(f()); 
参数 Dart 函数有两种参数,必要参数和可选参数,必要参数放到前面。可选参数可以是命名的或位置的,可选参数如果不传就为 null ,如果可选参数类型为不可空类型,就需要给可选参数提供默认值。
命名参数 命名参数默认为可选参数,使用 {} 包起来,除非被标记为 required
Copy 1 
2 
3 
4 
5 
6 
f({required  String ?  first, String ?  second}) {
   print(first);
   print(second);
 }
 f(first:  "FIRST" );
 f(first:  "FIRST" , second:  "SECOND" ); 
Copy 1 
2 
3 
4 
FIRST
 null
 FIRST
 SECOND 
位置参数 位置参数使用 [] 包起来
Copy 1 
2 
3 
4 
5 
6 
7 
f(String  must, [String ?  first]) {
   print(must);
   print(first);
 }
 
 f("must" );
 f("MUST" , "FIRST" ); 
Copy 1 
2 
3 
4 
must
 null
 MUST
 FIRST 
main 函数 main 函数作为入口,返回值为 void ,有一个 List<String> 的可选参数。
Copy 1 
2 
3 
void  main() {
  print("Hello World" );
 } 
Copy 1 
2 
3 
void  main(List< String >  args) {
  print(args);
 } 
匿名函数 格式与命名函数类似
Copy 1 
2 
3 
4 
var  f =  () {
  return  10 ;
 };
 print(f()); 
词法闭包 函数可以封闭定义到它作用域的变量。
Copy  1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
/// Returns a function that adds [addBy] to the
 /// function's argument.
 int  addBy) {
  return  (int  i) =>  addBy +  i;
 }
 
 void  main() {
  // Create a function that adds 2.
  var  add2 =  makeAdder(2 );
   // Create a function that adds 4.
  var  add4 =  makeAdder(4 );
   print(add2(3 ) ==  5 );
   print(add4(3 ) ==  7 );
 } 
运算符 算术运算符:=+= - -表达式 * / ~/(除并向下取整) % ++ --
关系运算符:==== != > < >= <=
判断两个对象是否相同一般用 == 就可以了,及少数情况需要 identical
类型判断符:
as ,类型转换 is ,为指定类型返回 true is! ,为指定类型返回 flase 赋值运算符:=== ??=
Copy 1 
2 
3 
4 
// Assign value to a
 =  value;
// 如果 b 是 null ,b 为 value ,否则 b 保持原值
 ??=  value;
算数运算符同样可以和赋值运算符结合,如:=+== -= 等
逻辑运算符:
按位和移位运算符:=&= | ^ ~ <<
>> >>>
条件表达式:=?:= ??
根据布尔表达式赋值时用 ?:
Copy 1 
var  a =  isPub ?  "A"  :  "B" ;
根据 null 赋值时用 ??
Copy 1 
String  playerName(String ?  name) =>  name ??  "Default" ;
级联运算符:=..= ?.. 可以在同一对象上连续调用多个对象的变量或方法
Copy 1 
2 
3 
4 
var  paint =  Paint()
..color =  Colors.black
 ..strokeCap =  StrokeCap.round
 ..strokeWidth =  5.0 ; 
等价于:
Copy 1 
2 
3 
4 
var  paint =  Paint();
paint.color =  Colors.black;
 paint.strokeCap =  StrokeCap.round;
 paint.strokeWidth =  5.0 ; 
控制流 条件 if-else 进行条件判断,else 是可选的,if 语句中的条件必须是布尔值
Copy 1 
2 
3 
4 
5 
6 
7 
8 
var  n =  1 ;
if  (n <=  0 ) {
  print("<=0" );
 } else  if  (n <=  1 ) {
   print("<=1" );
 } else  {
   print("else" );
 } 
Dart 支持 switch-case ,非空的 case 子句必须有 break;
Copy  1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
var  command =  'OPEN' ;
switch  (command) {
  case  'OPEN' : 
     print("Open" );
     // ERROR: Missing break
    case  'CLOSED' : 
     print("Close" );
     break ;
 } 
Dart 支持空的 case
循环 普通的 for 循环
Copy 1 
2 
3 
for  (var  i =  0 ; i <  2 ; i++ ) {
  print(i);
 } 
for-in 遍历可迭代对象,如 List
Copy 1 
2 
3 
4 
var  list =  [1 , 2 ];
for  (final  e in  list) {
  print(e);
 } 
forEach 也可以作用于可迭代对象
Copy 1 
2 
var  list =  [1 , 2 ];
list.forEach(print); 
while , do-while 和其它语言一样
break 中断循环, continue 继续循环。
断言 Dart 的 assert 失败会抛出 AssertionError 异常,一般在生产环境会忽略
异常 Dart 可以抛出和捕获异常,推荐抛出 Error  和 Exception  类型的异常
抛出异常:
Copy 1 
throw  "Any type exception" ;
使用 try on catch 来捕获异常
Copy  1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
try  {
  breedMoreLlamas();
 } on OutOfLlamasException {
   // A specific exception
  } on Exception catch  (e) {
   // Anything else that is an exception
  'Unknown exception:  $ e' );
} catch  (e) {
   // No specified type, handles all
  'Something really unknown:  $ e' );
} 
on 指定异常类型,=catch= 捕获异常对象,catch 的第二参数可以为堆栈信息。
Copy 1 
2 
3 
4 
5 
6 
7 
8 
try  {
  // ···
  catch  (e) {
  print('Exception details: \n   $ e' );
 } catch  (e, s) {
   print('Exception details: \n   $ e' );
   print('Stack trace: \n   $ s' );
 } 
rethrow 可以再次抛出异常
finally 语句会始终执行
类 Dart 是基于 mixin 继承机制的面向对象语言,除了 null 外,所有的类都继承自 Object 类。
使用类 访问类成员 使用 . 运算符访问类的成员,使用 ?. 同样可以访问类的成员,但是可以避免左边表达式为 null
产生问题。
Copy  1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
var  p =  Point(2 , 2 );
 // Get the value of y.
 assert (p.y ==  2 );
 // Invoke distanceTo() on p.
 double  distance =  p.distanceTo(Point(4 , 4 ));
 // If p is non-null, set a variable equal to its y value.
 var  a =  p? .y;
构造函数 可以使用构造函数创建一个对象,构造函数可以为类名或者类名.标识符的方式:
Copy 1 
2 
var  p1 =  Point(2 , 2 );
var  p2 =  Point.fromJson({'x' :  1 , 'y' :  2 });
类可以提供常量构造函数,来提供编译时常量
Copy 1 
var  p =  const  ImmutablePoint(2 , 2 );
实现类 实例变量 Copy 1 
2 
3 
4 
5 
class  Point  {
  double ?  x;
   double ?  y;
   double  z =  0 ;
 }; 
未初始化的实例变量被初始化成 null
所有实例变量都会有一个 get 方法,非 final 的实例变量和 later final 声明但是为初始化的实例变量还会有一个 set 方法
Copy  1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
class  Point  {
  double ?  x; // Declare instance variable x, initially null.
  double ?  y; // Declare y, initially null.
 
 void  main() {
  var  point =  Point();
   point.x =  4 ; // Use the setter method for x.
  ==  4 ); // Use the getter method for x.
 ==  null ); // Values default to null.
 
构造函数 声明一个和类名一样的函数即为构造函数
Copy  1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
class  Point  {
  double  x =  0 ;
   double  y =  0 ;
 
   Point(double  x, double  y) {
     // There's a better way to do this, stay tuned.
  this .x =  x;
    this .y =  y;
   }
 } 
一种简化的方法:
Copy 1 
2 
3 
4 
5 
6 
7 
8 
class  Point  {
  double  x =  0 ;
   double  y =  0 ;
 
   // Syntactic sugar for setting x and y
  // before the constructor body runs.
 this .x, this .y);
} 
构造函数可以有初始化列表
Copy 1 
2 
3 
4 
5 
6 
7 
8 
// Initializer list sets instance variables before
 // the constructor body runs.
 < String , double >  json)
    :  x =  json['x' ]! ,
       y =  json['y' ]!  {
   print('In Point.fromJson(): ( $ x,  $ y)' );
 }
 //  等号右边不能用  this 
如果没有声明构造函数,类会自动生成一个无参数构造函数,并会调用父类的无参数构造函数。
子类不会继承父类的构造函数。
可以为一个类声明多个命名式构造函数,让语义更加清晰
Copy  1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
const  double  xOrigin =  0 ;
const  double  yOrigin =  0 ;
 class  Point  {
  double  x =  0 ;
   double  y =  0 ;
 
   Point(this .x, this .y);
 
   // Named constructor
        :  x =  xOrigin,
         y =  yOrigin;
 } 
子类和父类构造函数调用顺序:
子类构造函数的初始化列表 父类构造函数 当前类的构造函数 默认会调用父类的无参数构造函数,除非显示调用父类构造函数。
Copy  1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
  class  Person  {
   String ?  firstName;
 
   Person.fromJson(Map data) {
     print('in Person' );
   }
 }
 
 class  Employee  extends  Person {
  // Person does not have a default constructor;
  // you must call super.fromJson(data).
 :  super .fromJson(data) {
    print('in Employee' );
   }
 }
 
 void  main() {
  var  employee =  Employee.fromJson({});
   print(employee);
   // Prints:
  // in Person
 // in Employee
 // Instance of 'Employee'
 
Copy 1 
2 
3 
in Person
 in Employee
 Instance of 'Employee' 
传递给父类的参数不能有 this ,因为子类构造函数还没有执行 
构造函数可以重定向
Copy 1 
2 
3 
4 
5 
6 
7 
8 
9 
class  Point  {
  double  x, y;
 
   // The main constructor for this class.
  this .x, this .y);
   // Delegates to the main constructor.
  double  x) :  this (x, 0 );
} 
如果类生成的对象是不可变的,可以定义常量构造函数
Copy 1 
2 
3 
4 
5 
6 
7 
class  ImmutablePoint  {
  static  const  ImmutablePoint origin =  ImmutablePoint(0 , 0 );
 
   final  double  x, y;
 
   const  ImmutablePoint(this .x, this .y);
 } 
使用 factory 标记的构造函数会让该构造函数称为工厂构造函数,意味着使用工厂构造函数不一定总是返回新的实例
Copy  1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
class  Logger  {
  final  String  name;
   bool  mute =  false ;
 
   // _cache is library-private, thanks to
  // the _ in front of its name.
 static  final  Map< String , Logger>  _cache = 
      < String , Logger> {};
 
   factory  Logger(String  name) {
     return  _cache.putIfAbsent(
         name, () =>  Logger._internal(name));
   }
 
   factory  Logger.fromJson(Map< String , Object >  json) {
     return  Logger(json['name' ].toString());
   }
 
   Logger._internal(this .name);
 
   void  log(String  msg) {
     if  (! mute) print(msg);
   }
 } 
方法 实例方法可以访问实例变量和 this
Copy  1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
import  'dart:math' ;
 class  Point  {
  double  x =  0 ;
   double  y =  0 ;
 
   Point(this .x, this .y);
 
   double  distanceTo(Point other) {
     var  dx =  x -  other.x;
     var  dy =  y -  other.y;
     return  sqrt(dx *  dx +  dy *  dy);
   }
 } 
Dart 可以重写部分操作符
Copy  1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
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.
  // ···
 
 void  main() {
  final  v =  Vector(2 , 3 );
   final  w =  Vector(2 , 2 );
 
   print(v +  w ==  Vector(4 , 5 ));
   print(v -  w ==  Vector(0 , 1 ));
 } 
Getter 和 Setter 是读写对象属性的特殊方法,可以用 get set 关键字为额外属性定义 Getter 和 Setter 方法
Copy  1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
class  Rectangle  {
  double  left, top, width, height;
 
   Rectangle(this .left, this .top, this .width, this .height);
 
   // Define two calculated properties: right and bottom.
  double  get  right =>  left +  width;
  set  right(double  value) =>  left =  value -  width;
   double  get  bottom =>  top +  height;
   set  bottom(double  value) =>  top =  value -  height;
 }
 
 void  main() {
  var  rect =  Rectangle(3 , 4 , 20 , 15 );
   print(rect.left ==  3 );
   rect.right =  12 ;
   print(rect.left ==  - 8 );
 } 
抽象方法是定义接口而没有具体的实现,只会存在于抽象类中,直接用 ; 代替方法体可以声明一个抽象方法
Copy  1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
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 标识的类可以让该类称为抽象类,抽象类无法实例化。如果想让抽象类可以实例化,可以定义一个工厂构造函数
Copy 1 
2 
3 
4 
5 
6 
7 
// This class is declared abstract and thus
 // can't be instantiated.
 abstract  class  AbstractContainer  {
  // Define constructors, fields, methods...
    void  updateChildren(); // Abstract method.
  
隐式接口 如果想要 A 类支持调用 B 类的 API 且不想继承 B 类,则可以实现 B 类的接口
一个类可以通过关键字 implements 来实现一个或多个接口并实现每个接口定义的 API
Copy  1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
// A person. The implicit interface contains greet().
 class  Person  {
  // In the interface, but visible only in this library.
  final  String  _name;
   // Not in the interface, since this is a constructor.
  this ._name);
   // In the interface.
  String  greet(String  who) =>  'Hello,  $ who. I am  $ _name.' ;
}
 
 // An implementation of the Person interface.
 class  Impostor  implements  Person {
  String  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()));
 } 
Copy 1 
2 
Hello, Bob. I am Kathy.
 Hi Bob. Do you know who I am? 
要实现多个接口,可以用逗号分割每个接口类
Copy 1 
class  Point  implements  Comparable, Location {...}
继承 使用 extends 来创建一个子类,使用 super 关键字在子类中引用父类
Copy  1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
class  Television  {
  void  turnOn() {
     _illuminateDisplay();
     _activateIrSensor();
   }
   // ···
  
 class  SmartTelevision  extends  Television {
  void  turnOn() {
     super .turnOn();
     _bootNetworkInterface();
     _initializeMemory();
     _upgradeApps();
   }
   // ···
  
子类可以重写父类的实例方法,可以使用 @override 注解表示重写了一个成员
Copy  1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
class  Television  {
  // ···
  set  contrast(int  value) {...}
}
 
 class  SmartTelevision  extends  Television {
  @ override
   set  contrast(num  value) {...}
   // ···
  
重写的方法需要满足下面几点:
返回值类型必须是父类方法返回值的相同类型或父类型 参数类型同返回值类型 父类方法接收 n 个位置参数,重写方法也要接收 n 个位置参数 泛型方法不能重写非泛型方法,非泛型方法不能重写泛型方法 noSuchMethod 如果调用了对象上不存在的方法或实例变量,会触发 noSuchMethod 方法,可以重写这个方法
只有下面一个条件成立时,才能调用到 noSuchMethod :
接收方是静态的 dynamic 类型 接收方具有静态类型,定义了未实现的方法,并且接收方的动态类型实现了 noSuchMethod 方法。 Copy  1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
class  A  {
  @ override
   void  noSuchMethod(Invocation invocation) {
     print("A: noSuchMethod" );
   }
 }
 
 class  B  {
  @ override
   void  noSuchMethod(Invocation invocation) {
     print("B: noSuchMethod" );
   }
 
   void  foo();
 }
 
 void  main() {
  dynamic  a =  A();
   a.method();
 
   B b =  B();
   b.foo();
 } 
Copy 1 
2 
A: noSuchMethod
 B: noSuchMethod 
扩展方法 扩展方法用来向一些已有的库添加额外的方法,比如下面向 String 添加一个 addPrefix 方法
Copy 1 
2 
3 
4 
5 
6 
7 
8 
9 
extension MyExt on String  {
   String  addPrefix(String  prefix) {
     return  prefix +  ": "  +  this ;
   }
 }
 
 void  main() {
  print("Hello" .addPrefix("Jack" ));
 } 
枚举 使用 enum 关键字定义枚举类型 ,每一个枚举值都有一个 index ,=values= 可以获取枚举值列表
Copy 1 
2 
3 
4 
5 
enum Color { red, green, blue }
 void  main() {
  print(Color.red.index);
   print(Color.values);
 } 
Copy 1 
2 
0
 [Color.red, Color.green, Color.blue] 
枚举的限制:
枚举不能称为子类,不可以 mixin ,也不可以实现枚举 不能实例化一个枚举 Mixin 使用 mixin 关键字定义 mixin ,使用 with 关键字使用 mixin ,普通类也可以作为 mixin 。
Copy  1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
class  Stream  {
 }
 
 mixin Readable {
   void  read() {
     print("Readable: read" );
   }
 }
 
 mixin Writable {
   void  write() {
     print("Writable: write" );
   }
 }
 
 class  ReadWriteStream  extends  Stream  with  Readable, Writable{
 }
 
 void  main() {
  var  stream =  ReadWriteStream();
   stream.read();
   stream.write();
 } 
Copy 1 
2 
Readable: read
 Writable: write 
类变量和方法 使用 static 关键字声明类的变量和方法
Copy  1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
class  A  {
  static  String  name =  "A" ;
 
   static  String  GetName() {
     return  name;
   }
 }
 
 void  main() {
  print(A.name);
   print(A.GetName());
 } 
泛型 Dart 的方法和类都支持泛型,通过 extends 限制泛型类型
Copy  1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
class  A < T extends  num >  {
  num  add(T first, T second) {
     return  first +  second;
   }
 }
 
 num  add< T extends  num > (T first, T second) {
  return  first +  second;
 }
 
 void  main() {
  print(add(2 , 2.5 ));
 
   var  a =  A();
   print(a.add(2.5 , 3 ));
 } 
库 使用 import 和 library 关键字可以创建一个模块化和可共享的库以 _ 开头的成员只在库内部可见。
使用库 使用 import 导入库,它的参数是代码库的 URI ,对于内置库,使用 dart:xxxx 形式,对于其它库,可以使用 package:xxxxx 或者直接是文件路径
Copy 1 
2 
import  "dart:html" 
import "package:test/test.dart"  
如果库有冲突的标识符,可以指定前缀
Copy 1 
2 
3 
4 
5 
6 
7 
import  "package:lib1" ;
import  "package:lib2"  as  lib2;
 // lib1
 =  A();
// lib2
 =  lib2.A();
可以只导入库的一部分
Copy 1 
2 
3 
4 
// 只导入 foo
 import  "package:lib1"  show  foo
//  导入所有,除了  foo
import "packaeg:lib2"  hide  foo 
实现库 这一部分会单独写一篇文章
异步 Dart 中有两种异步对象: Future 和 Stream ,返回这两种对象的函数都是异步函数。
Future 一个 Future<T> 表示一个异步操作的结果,它有两种状态:
当调用一个异步函数时,返回一个未完成态的 Future ,这个 Future 正在等待异步函数的结果 T ,如果异步函数完成,Future 会到完成状态并拿到结果,如果异步函数出现错误,
Future 同样会到达完成状态并获取到对应的错误。
Future<T> 的 then 方法会注册一个回调,当 Future 到达完成状态时会调用这个回调函数
Copy  1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
Future< String >  delayEcho(String  msg) {
   return  Future< String > .delayed(const  Duration(seconds:  1 ), () =>  msg);
 }
 
 Future< void >  delayError() {
   return  Future.delayed(const  Duration(seconds:  1 ), () =>  throw  "Delay Error" );
 }
 
 void  main() {
  var  result =  delayEcho("Hello" );
   result.then((msg) =>  print("Received: "  +  msg));
 
   var  error =  delayError();
   error.then((value) =>  print("finished" ), onError:  (error) =>  print("Error: "  +  error));
 } 
Copy 1 
2 
Received: Hello
 Error: Delay Error 
async 和 await async 可以让一个函数称为异步函数,在异步函数里可以用 await 等待 Future 结果,用 try-catch 捕获 Future 的错误。main 函数也可以成为一个异步函数
Copy  1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
Future< String >  delayEcho(String  msg) {
   return  Future< String > .delayed(const  Duration(seconds:  1 ), () =>  msg);
   }
 
 Future< void >  delayError() {
   return  Future.delayed(const  Duration(seconds:  1 ), () =>  throw  "Delay Error" );
 }
 
 void  main() async  {
  var  msg =  await  delayEcho("Use Await" );
   print(msg);
   try  {
     await  delayError();
   } catch  (e) {
     print("Error:  $ e" );
   }
 } 
Copy 1 
2 
Use Await
 Error: Delay Error 
Stream Stream 提供了一种异步的数据序列,包括用户生成的事件或从文件中读取的数据。
可以使用 async 关键字和 异步循环 await for 从 Stream 中获取值
Copy 1 
2 
3 
4 
5 
void  main() async  {
  await  for  (final  request in  requestServer) {
     handleRequest(request);
   }
 } 
生成器 当需要延迟生成一连串的值时,可以使用生成器函数:
同步生成器返回一个 Iterable 对象 异步生成器返回一个 Stream 对象 通过 sync* 关键字实现一个同步生成器函数,使用 yield 语句来传递值通过 async* 关键字实现一个异步生成器函数,使用 yield 语句来传递值
Copy  1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
Iterable< int >  g(int  n) sync *  {
   int  k =  0 ;
   while  (k <  n) yield  k++ ;
 }
 
 Stream< int >  ga(int  n) async *  {
   int  k =  0 ;
   while  (k <  n) yield  k++ ;
 }
 
 void  main() async  {
  for  (final  n in  g(3 )) {
     print(n);
   }
 
   await  for  (final  n in  ga(3 )) {
     print(n);
   }
 } 
元数据 使用元数据可以为代码增加额外的信息。元数据注解以 @ 开头,后面跟一个编译时常量,或者调用一个常量构造函数。
自定义元数据注解
Copy  1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
10 
class  Todo  {
  final  String  who;
   final  String  what;
 
   const  Todo(this .who, this .what);
 }
 
 @ Todo("Jack" , "change function name" )
void  f() {
}