1. 概述
Dart 是 Google 下一代操作系統(tǒng) Fuchsia 的御用程序開發(fā)語(yǔ)言,而是 App 跨平臺(tái)框架 Flutter 使用的開發(fā)語(yǔ)言。它是一種面向?qū)ο蟮恼Z(yǔ)言导俘,使用 C 風(fēng)格語(yǔ)法妓笙,揉合了 Javascript宰衙、Python、Java 等語(yǔ)言的相關(guān)特性闯估。
如果之前熟悉這幾門語(yǔ)言灼舍,可以快速入門。
2. 應(yīng)知就會(huì)
2.1. 語(yǔ)句結(jié)束符
同 C 語(yǔ)言一樣涨薪,Dart 所有語(yǔ)句都以 ;
結(jié)束骑素。
2.2. 注釋
同 Javascript,單行用 //
, 多行用 /* */
刚夺。
如果要支持文檔生成工具献丑,則單行注釋用 ///
,多行注釋用 /** */
, 例如:
/// This is a documentation comment
/**
This , too,
is a
documentation comment.
*/
2.3. 變量聲明與類型
Dart 是強(qiáng)類型語(yǔ)言侠姑,即所有變量都是有類型的创橄。
同 C 語(yǔ)言一樣,可以在聲明變量時(shí)指定類型 <specific_type> variable;
莽红,如:
int a = 3;
也可以同 Javascript 中一樣妥畏,用 var a = 3;
聲明,此時(shí) Dart 會(huì)從賦值語(yǔ)句的右值推導(dǎo)出變量的類型安吁,如本例中將推導(dǎo)出 a
變量是 int
型醉蚁。
那么之后該變量的類型就確定了,不能再賦值其它類型的值了鬼店,例如本例中再給 a 賦值字符串值編譯會(huì)通不過: a = "String Value";
网棍。
如果將變量聲明為 dynamic
類型,那么當(dāng)變量賦值為一個(gè)類型的值后妇智,可隨時(shí)再次賦值為其它類型的值确沸,如:
dynamic x = 42;
x = "Hello World";
同 Java 一樣, Dart 中的一切都是對(duì)象俘陷,都最終繼承自 Object
類罗捎,因此將變量聲明為 Object
類型類似于聲明為 dynamic
類型,可以賦值任何類型的值拉盾,如:
Object x = 42;
x = "Hello World";
兩都區(qū)別是: dynamic
類型告訴 Dart 不要查檢變量的類型桨菜,即關(guān)閉類型檢測(cè)功能,因此若引用 dynamic 變量上不存在的方法時(shí),編譯是能通過的倒得,但在運(yùn)行時(shí)出錯(cuò); 而引用
Object
類上不存在的方法時(shí)泻红,編譯就不能通過。
2.4. 常量和 final 值
同 Java 類似霞掺,常量值用 const
修飾谊路,常量值在編譯時(shí)就被確定,不能修改菩彬,如:
const x = "Hello";
const String y = "world";
而 final
變量可以在運(yùn)行時(shí)賦值缠劝,但只能賦值一次,例如想確定程序啟動(dòng)時(shí)間值骗灶,可以聲明為一個(gè) final
變量惨恭,如:
final x = DateTime.now();
const
不僅能修飾變量,也可以修飾值耙旦,如:
List lst = const [1, 2, 3];
lst[0] = 999; // compile error
2.5. 類型
基本類似用小寫開頭脱羡,如 int, double, num, bool
, 其中 num
是 int, double
的父類。其它類型以大寫開頭免都,如 String, List, Map
锉罐。
2.5.1. 數(shù)字型
同 C 一樣,int, double, num
都支持 +, -, *, /, %
绕娘。數(shù)字型唯一特殊的操作符是 ~/
脓规,表示返回除法結(jié)果的整數(shù)部分,功能同 Python 中的 //
操作符业舍,如:
int x = 3;
double y = 2.0;
x = x ~/ y;
print(x); // 1
同 C 中一樣,dart 中 x = x + y
也可以縮寫為 x += y
升酣,這種縮寫同樣適用于 -, *, /, %
和 ~/
操作符舷暮,如 x = x ~/ y
可寫為 x ~/= y
。
也有 C 中類似的 v++
, ++v
, v--
, --v
等前后綴操作符及三元條件表達(dá)式如: x = a ? b : c
噩茄。
Dart 中還支持一種特有的二元條件表達(dá)式下面,如 x = a ?? b;
,表示當(dāng) a 有值時(shí)(即不為 null 值热芹,聲明的變量未初始化時(shí)值默認(rèn)為 null)唉地,x 賦值為 a戒劫,否則賦值為 b。
2.5.2. 字符串型 String
同 Javascript 中類似机杜,字符串即可以用單引號(hào) 'xxx'
,也可以用雙引號(hào) "xxx"
表示衅谷。字符串中若包含 ${val}
形式的變量引用椒拗,則生成的字符串結(jié)果中會(huì)自動(dòng)用變量值進(jìn)行替換,這是一種很方便的功能,如:
String name = "Haiiiiiyun";
String greeting = "Hello ${name}"; // Hello Haiiiiiyun
String greeting = "Hello $name"; // 也可省略 {}蚀苛,直接用 $var
字符串與數(shù)字間可進(jìn)行相互轉(zhuǎn)換在验,如:
int i = 42;
double d = 4.2;
String si = i.toString(); // "42"
String sd = d.toString(); // "4.2";
int i2 = int.parse(si); // 42
double d2 = double.parse(sd); // 4.2
2.5.3. 布爾型 bool
只有 true
和 false
兩個(gè)值,注意堵未,在 if while 等條件判斷表達(dá)式的值不能隱匿轉(zhuǎn)換成 bool
類型值腋舌,這和一般的語(yǔ)言都不同,例如:
if (1) { //
print("true");
}
不能編譯通過渗蟹,因?yàn)?1
不能轉(zhuǎn)換成 bool
型值 true
块饺。
2.5.4. 枚舉型 Enum
類型 C,可用 enum
定義枚舉類型拙徽,如: enum Week{ Mon, Tue, Wed, Thu, Fri, Sat, Sun };
刨沦。
其每個(gè)值都有一個(gè) index 值,如 Week.Mon.index
的值為 0
膘怕,Week.Tue.index
值為 1想诅,以此類推。枚舉值適用于 switch
語(yǔ)句岛心。
2.5.5. List
List 類似 Javascript 中的數(shù)組来破,它是有序的,因此有 indexOf
方法忘古,操作有:
List lst = ['a', 'b', 'c'];
lst.add('d'); // ['a', 'b', 'c', 'd']
lst.removeLast(); // ['a', 'b', 'c']
print(lst.indexOf('a')); // 0
Set 和 Python 中的 Set 類似徘禁,是無序數(shù)組,且其中的元素不會(huì)重復(fù):
Set chars = Set();
chars.addAll([ "a", "b", "c" ]);
chars.add("a"); // 還是 ["a", "b", "c"]
chars.remove("b"); // ["a", "c"]
chars.contains("a")); // true
chars.containsAll([ "a", "b" ])); // false
2.5.6. Map
類似 Javascript 中的 dict髓堪,操作有:
Map<String, String> countries = Map();
countries['India'] = "Asia";
countries["Germany"] = "Asia"; // wrong
countries["France"] = "Europe";
countries["Brazil"] = "South America";
if (countries.containsKey("Germany")) {
countries["Germany"] = "Europe"; // update
print(countries); // {India: Asia, Germany: Europe, France: Europe, Brazil: South America}
}
countries.remove("Germany");
print(countries); // {India: Asia, France: Europe, Brazil: South America}
2.6. 類型測(cè)試和類型轉(zhuǎn)換
用 is
關(guān)鍵字進(jìn)行類型測(cè)試送朱,用 as
關(guān)鍵字進(jìn)行類型轉(zhuǎn)換,如
// Pig 是 Animal 的子類
if(animal is Pig) {
(animal as Pig).oink(); //叫聲
}
2.7. 流程控制
if
, else
和 C 中完全一樣干旁。條件表達(dá)式也可用 ||
, &&
和 !
進(jìn)行組合驶沼。
switch
也和 C 中一樣。
2.8. 循環(huán)
while
和 do while
循環(huán)和 C 中完全一樣争群。同時(shí) continue
和 break
語(yǔ)句也一樣使用回怜。
2.8.1 for 循環(huán)
第一個(gè) for 循環(huán)形式和 C 類型,如:
for (var i=0; i<10; i++){
print(i);
}
第二個(gè) for-in
形式適用于可迭代對(duì)象换薄,如 List, Iterator 等:
List lst = ['a', 'b', 'c'];
for (var char in lst) {
print(char);
}
2.9 異常處理
異常處理和 Java 類似玉雾。基類是 Error
和 Exception
轻要,若要捕獲某種類型的異常复旬,用 on <SpecificException> catch(e)
,
若不關(guān)心捕獲的異常類型冲泥,直接用 catch(e)
赢底,而 finally
段中的代碼不管有沒有異常捕獲到最會(huì)最后執(zhí)行,如:
try {
somethingThatMightThrowAnException();
} on FormatException catch (fe) {
print(fe);
} on Exception catch (e) {
Print("Some other Exception: " + e);
} catch (u) {
print("Unknown exception");
} finally {
print("All done!");
}
3. 函數(shù)與對(duì)象
Dart 中所有變量都是對(duì)象,因此函數(shù)也是對(duì)象幸冻,其類型是 Function
粹庞。
函數(shù)定義同 C 中類似,如:
int add(int a, int b)
{
return a + b;
}
即定義函數(shù)時(shí)要指定函數(shù)返回值類型洽损,及參數(shù)的類型庞溜。如果沒有指定返回值類型,默認(rèn)為 void碑定。
類似 C流码, main(List<String> args)
函數(shù)是程序的入口函數(shù)。
3.1 命名函數(shù)參數(shù)
普通函數(shù)調(diào)用時(shí)延刘,其參數(shù)值由其位置確定漫试,如上面的函數(shù)調(diào)用:
int x = add(1, 2); // 3
命名函數(shù)參數(shù)在 {}
中指定,如:
int add2(int init, {int a, int b}){
return init + a + b;
}
print(add2(1, b:2, a:1)); // 4
其中的命名參數(shù)值在調(diào)用時(shí)用形如 x:y
形式指定碘赖,并且參數(shù)位置與定義時(shí)的位置無關(guān)驾荣,但必須在普通位置函數(shù)值之后。
3.2. 參數(shù)默認(rèn)值
可以指定命名參數(shù)的默認(rèn)值普泡,如:
int add3({int init=0, int a, int b}){
return init+a+b;
}
print(add3(a:1, b:2)); // 3
print(add3(init:10, a:1, b:2)); // 13
3.3. 可選參數(shù)
將參數(shù)放在 []
指定其為可選參數(shù)播掷,在調(diào)用時(shí)可不用提供參數(shù)值,如:
String add4(int a, int b, [String c]){
return "$c: ${a+b}";
}
main(){
print(add(1,2));
print(add2(1, a:1,b:2));
print(add3(a:1, b:2));
print(add3(init:10, a:1, b:2));
print(add4(1,2)); // null: 3
print(add4(1,2, "Sum")); // Sum: 3
}
3.4. 匿名函數(shù)
類似 Javascript 和 Java 中的匿名函數(shù)撼班,創(chuàng)建的匿名函數(shù)可以賦值給 Function 的變量歧匈,之后該變量和普通的函數(shù)一樣調(diào)用,如:
Function add5 = (int a, int b) {
return a+b;
};
print(add5(1,2)); // 3
如果函數(shù)體如本例一樣砰嘁,只是一條 return 語(yǔ)句件炉,則可以簡(jiǎn)寫為 fat arrow
形式(和 Python lambda 函數(shù)類似):
Function add6 = (int a, int b) => a+b;
print(add6(1,2)); // 3
3.5. 高階函數(shù)
函數(shù)是對(duì)象,可以作為函數(shù)參數(shù)值傳遞矮湘。而能接受函數(shù)值作為參數(shù)的函數(shù)即為高階函數(shù)斟冕。
例如:
Function add6 = (int a, int b) => a+b;
int operator(Function op_fun, int a, int b)
{
return op_fun(a, b);
}
print(operator(add6, 1, 2)); //3
這里的 operator
函數(shù)即為高階函數(shù)。
3.6. 閉包 Closure
閉包是一個(gè)特殊的函數(shù)板祝,也叫閉包函數(shù)宫静。其特點(diǎn)時(shí):當(dāng)定義閉包函數(shù)時(shí)走净,閉包函數(shù)將定義函數(shù)時(shí)其父作用域中的變量值都固定下來券时,從而當(dāng)調(diào)用閉包函數(shù)時(shí),使用的也是定義時(shí)的變量值伏伯。
這在函數(shù)里定義閉包函數(shù)時(shí)特別明顯(函數(shù)體中可以定義函數(shù))橘洞,如:
Function adder(int step)
{
return (a) => a + step;
}
var adder1 = adder(1);
print(adder1(1)); // 2
var adder10 = adder(10);
print(adder10(1)); //11
閉包函數(shù) adder1
將 step 值固定為定義時(shí)的 1
,而 adder10
將 step 值固定為 10
说搅。
4. 類與面向?qū)ο?/h1>
同 Java 類似炸枣,類用 class
定義,如:
class Animal {
int numLegs = 0;
int numEyes = 0;
Animal(int numLegs, int numEyes){
this.numLegs = numLegs;
this.numEyes = numEyes;
}
void eat() {
print("Animals eat everything depending on what type it is.");
}
}
var a1 = new Animal(4, 2);
var a2 = Animal(4, 2); // new 關(guān)鍵字可省略
創(chuàng)建類實(shí)例也用 new ClassName(arg)
,其中的 new
關(guān)鍵字可省略适肠,這樣創(chuàng)建類實(shí)例和調(diào)用函數(shù)在形式上就一樣了霍衫。
4.1 屬性和方法
其中的 numLegs
和 numEyes
是屬性,可以直接訪問實(shí)例中的屬性值侯养,如:
var a1 = new Animal(4, 2);
a1.numLegs = 2;
print(a1.numLegs); //2
其中的 eat()
是方法敦跌,可以和函數(shù)調(diào)用一樣調(diào)用類實(shí)例上的方法。
訪問屬性和方法時(shí)逛揩,如果對(duì)象為空柠傍,會(huì)拋出異常,類似 Typescript, Dart 中的 ?.
操作符用來訪問對(duì)象屬性時(shí)辩稽,如果對(duì)象為 null惧笛,則不訪問,
從而避免了拋出異常:
var a1 = Animal(4, 2);
a1 = null;
print(a1?.numLegs); //不會(huì)拋出異常逞泄。
靜態(tài)屬性和靜態(tài)方法都用 static
修改患整,它們是類級(jí)別的屬性和方法,可以直接在類上訪問:
class Circle {
static const pi = 3.14;
static void drawCicle() {
//...
}
}
print(Circle.pi); // 3.14
Circle.drawCicle();
4.2. 構(gòu)造方法
類中和類名相同的方法是構(gòu)造方法炭懊,構(gòu)造方法可以有參數(shù)并级,例如本例中的構(gòu)造方法,其功能只是設(shè)置類的各屬性值侮腹,像這種構(gòu)造方法在 Dart 中可以簡(jiǎn)寫為:
class Animal {
int numLegs;
int numEyes;
Animal(int this.numLegs, int this.numEyes);
void eat() {
print("Animals eat everything depending on what type it is.");
}
}
可定義多個(gè)構(gòu)造方法嘲碧,方法名要么和類名 <ClassName>
相同,要么以 <ClassName>.
為前綴父阻,例如:
class Animal {
int numLegs = 0;
int numEyes = 0;
Animal(int this.numLegs, int this.numEyes); //構(gòu)造方法
Animal.namedConstructor(int this.numLegs, int this.numEyes) { //另一個(gè)構(gòu)造方法
}
void eat() {
print("Animals eat everything depending on what type it is.");
}
}
4.2.1. 默認(rèn)構(gòu)造方法
和 C++ 類似愈涩,如果沒有聲明構(gòu)造方法,編譯器會(huì)自動(dòng)生成一個(gè)無參數(shù)的默認(rèn)構(gòu)造方法加矛,該默認(rèn)構(gòu)造方法也會(huì)調(diào)用父類的無參構(gòu)造方法履婉。
4.2.2. 構(gòu)造方法不能繼承
子類不能繼承父類的構(gòu)造方法。子類的構(gòu)造方法通過 super
調(diào)用父類的構(gòu)造方法斟览,并且和 C++ 中類似毁腿,將這種父類調(diào)用放在初始化列表中,如:
class Cat extends Animal {
Cat(): super(4,2) {
}
}
初始化列表中的代碼是最先執(zhí)行的苛茂。
4.2.3. 重定向構(gòu)造方法
一個(gè)構(gòu)造方法通過初始化列表已烤,只調(diào)用另一個(gè)構(gòu)造方法,沒有方法體妓羊,如:
class Animal {
int numLegs = 0;
int numEyes = 0;
Animal(int this.numLegs, int this.numEyes); //構(gòu)造方法
Animal.cat(): this(4, 2); // 重定向構(gòu)造方法
void eat() {
print("Animals eat everything depending on what type it is.");
}
}
4.2.4. getter 和 setter
import 'dart:math';
class Square {
double width;
Square(this.width);
double get area => width * width;
set area(inArea) => width = sqrt(inArea);
}
var s = Square(5);
print(s.area); // 25.0
s.area = 16;
print(s.width); // 4.0
用 get
關(guān)鍵字設(shè)置 getter, 用 set
關(guān)鍵字設(shè)置 setter胯究。
4.3. 子類
和 Java 一樣,生成子類的語(yǔ)法是 class SubClass extends SuperClass {}
躁绸。
Dart 不支持多重繼承裕循,只能 extends 自一個(gè)父類臣嚣。
4.4. 抽象類
類似 Java, 抽象類用 abstract
修飾,抽象類中可以只聲明方法剥哑,也可以有默認(rèn)的實(shí)現(xiàn):
abstract class Shape {
double area(); //只有聲明
void draw(){ //有默認(rèn)實(shí)現(xiàn)體
print("draw here");
}
}
抽象類不能實(shí)例化硅则。
4.4. 接口 interface
Dart 中類與接口不分,普通類和抽象類都是接口株婴。類不能多重繼承抢埋,但可以實(shí)現(xiàn)多個(gè)接口,用 implements
關(guān)鍵字督暂,多個(gè)接口用 ,
分隔揪垄,如:
abstract class Shape {
double area(); //只有聲明
void draw(){ //有默認(rèn)實(shí)現(xiàn)體
print("draw here");
}
}
class Circle implements Shape {
@override
double area(){
}
@override
void draw(){ //有默認(rèn)實(shí)現(xiàn)體
print("draw here");
}
}
如果用 implements
實(shí)現(xiàn)接口,則必須實(shí)現(xiàn)(重寫)接口中的全部方法和屬性逻翁,不管接口中有沒有默認(rèn)實(shí)現(xiàn)饥努。
4.6. mixin
mixin 是實(shí)現(xiàn)代碼復(fù)用的一種方式。
Dart 中類與mixin不分八回,普通類和抽象類都是mixin酷愧。類不能多重繼承,但可以引入多個(gè)mixin缠诅,用 with
關(guān)鍵字溶浴,多個(gè)mixin用 ,
分隔,如:
class Shape {
double area() {
}
void draw(){
print("draw here");
}
}
class Circle with Shape {
}
var c = Circle();
c.draw();
同接口不同管引,子類無需實(shí)現(xiàn) mixin 中已實(shí)現(xiàn)了的方法士败。
4.7. 特殊方法
4.7.1 call()
如果類中實(shí)現(xiàn)了 call 方法,則類實(shí)例可以像函數(shù)一樣調(diào)用褥伴,而實(shí)際上執(zhí)行的就是實(shí)例中的 call(...)
方法谅将,如:
class CallableClassWithoutArgument {
String output = "Callable class";
void call() {
print(output);
}
}
class CallableClassWithArgument {
call(String name) => print("$name");
}
var withoutArgument = CallableClassWithoutArgument();
withoutArgument(); // Callable class
var withArgument = CallableClassWithArgument();
print(withArgument("John Smith")); // John Smith
4.7.2 操作符重載方法
Dart 類中可以重載以下操作符:<, >, <=, >=, -, +, /, ~/, *, %, |, ^, &, <<, >>, [], []=, ~, ==
。
重載方法是在類中實(shí)現(xiàn) operator
及操作符號(hào)為名的方法重慢,例如:
class MyNumber {
num val;
num operator + (num n) => val * n;
MyNumber(this.val);
}
MyNumber mn = MyNumber(5);
print(mn+2); // 10
這里饥臂,將 + 功能重載為乘法功能。
5. 包
每個(gè) dart 文件都是一個(gè)包 package似踱。
用 import 'URL'
的形式導(dǎo)入其它包隅熙,其中的 URL
的形式為 schema:path
, schema 的值有:
-
dart
: Dart 內(nèi)置的包核芽,如import 'dart:math'
-
package
: 第三方包囚戚,如import 'package:flutter/material.dart'
- 沒有 schame部分:表示只導(dǎo)入當(dāng)前項(xiàng)目中的相關(guān)包,如
import 'NotesModel.dart'
可以用 show
關(guān)鍵字限定只導(dǎo)入包中相關(guān)屬性狞洋,如 import "NotesModel.dart" show NotesModel, notesModel;
弯淘。
也可以用 hide
關(guān)鍵字限定只排除包中相關(guān)屬性绿店,其它屬性都導(dǎo)入吉懊,如 import "NotesModel.dart" show NotesModel, notesModel;
庐橙。
如果包中的屬性名有沖突,可以用 as
關(guān)鍵字將導(dǎo)入的包重命名借嗽,如 import 'dart:math' as math;
态鳖。