一巡雨、開篇
dart 語言具有如下特性
- 一切變量皆是對象,每個對象都是類的實例暮现。int还绘、double、null送矩、函數(shù)等都是對象蚕甥,所有對象都繼承自 Object 類
- dart 是強類型語言,但由于具備類型推導功能所以類型聲明是可選的
- dart 支持頂級函數(shù)栋荸、靜態(tài)函數(shù)菇怀、實例函數(shù),也允許在函數(shù)中嵌套函數(shù)晌块,即局部函數(shù)爱沟。類似的,dart 也支持頂級變量匆背、靜態(tài)變量和實例變量
- dart 沒有關于 public呼伸、protected、private 的關鍵字。通過為變量標識符添加下劃線前綴括享,表明該對象是私有的
- 標識符以字母或下劃線開頭搂根,后跟任意字母和數(shù)字組合
看個小例子
/**
* 多行注釋
*/
void printString(String msg) {
print('msg value: $msg');
}
//單行注釋
void main() {
var msg = 'Hello, World!';
printString(msg); //msg value: Hello, World!
printString(null); //msg value: null
}
如上代碼包含了 dart 語言(也是基本所有的編程語言都具備的)的一些基本元素
- 多行注釋和單行注釋
- 以分號結尾且是必需的
- 允許定義頂層函數(shù)
- 最基礎的數(shù)據(jù)類型之一:String,以單引號或雙引號包裹起來铃辖。其它的內置數(shù)據(jù)類型還有 int 剩愧、double、list娇斩、map 等
- 類型推導仁卷。通過關鍵字 var 來聲明變量而無需指明變量類型
- 一種方便的插入變量值的方式,字符串字面值:$msg
- 應用程序的入口:main 函數(shù)
二犬第、變量
1锦积、變量聲明
與 Java 語言相比,dart 語言包含的類似的基本數(shù)據(jù)類型只有 int
和 double
兩種歉嗓,且這兩種類型的變量均是對象丰介,其默認值均為 null
本教程遵循官方風格指南建議,大部分例子都是使用 var 來聲明變量而不指明對象類型
void main() {
int intValue;
print(intValue); //null
intValue = 10;
print(intValue); //10
var varIntValue = 20;
print(varIntValue); //20
}
dart 是強類型語言鉴分,無法將一個已聲明具體變量類型的變量賦值為另一個無繼承關系的變量
例如基矮,以下代碼會導致報錯,因為無法將一個 double 值賦值到一個 int 類型變量上
int intValue = 20;
intValue = 20.0; //error
但由于 int
和 double
類都是 num 類的子類冠场,所以以下操作是合法的
num numValue = 10;
print(numValue.runtimeType); //int
numValue = 10.22;
print(numValue.runtimeType); //double
2、dynamic
dynamic
類似于 Java 中的 Object
本砰,dynamic
對象可以用來指向任意類型變量碴裙,非 null 的 dynamic 變量會有具體的運行時類型
dynamic value = "leavesC";
print(value.runtimeType); //String
value = 12121;
print(value.runtimeType); //int
3、final 和 const
如果你希望一個變量在賦值后其引用不能再改變点额,可以通過 final 或 const 這兩個關鍵字來實現(xiàn)
const 變量代表的是編譯時常量舔株,例如字面量就是一種編譯時常量,在編譯期还棱,程序運行前就有確定值了载慈,因此實例變量不能使用 const 修飾。如果 const 變量是類級別的珍手,需要標記為 static const
而 final 修飾的變量是運行時常量办铡,賦值后不能再改變引用,可以在運行時再賦予變量值琳要,因此實例變量能使用 final 修飾
void main() {
const URL = "https://github.com/leavesCZY/AndroidGuide";
var booleValue = true;
final name = getName(booleValue);
print(name);
}
String getName(boolValue) {
if (boolValue) {
return "leavesC";
} else {
return "leavesC =_=";
}
}
三寡具、內建類型
1、num
dart 的數(shù)字類型有 int 和 double 兩種稚补,這兩種都是 num 類的子類童叠。int 類型根據(jù)平臺的不同,整數(shù)值不大于64位课幕。在 Dart VM 上厦坛,值可以從 -263 到 263-1五垮,編譯成 JavaScript 的 Dart 使用JavaScript 代碼,允許值從 -253 到 253 - 1杜秸。double 類型即64位雙精度浮點數(shù)放仗,由 IEEE 754 標準指定
void main() {
var intValue = 100;
print(intValue.runtimeType); //int
var doubleValue = 100.0;
print(doubleValue.runtimeType); //double
num numValue = 100;
print(numValue.runtimeType); //int
numValue = 100.0;
print(numValue.runtimeType); //double
}
一些常見的數(shù)字類型轉換方法
print(num.parse("2000"));
print(int.parse("200"));
print(double.parse("121"));
2、string
除了可以通過單引號或者雙引號來聲明一個字符串外亩歹,也可以通過相鄰的字符串字面量來聲明一個組合字符串(相當于使用 + 把字符串相加為一個整體)匙监,此時可以混用單引號和雙引號,但建議只用單引號
var stringValue = "leavesC";
var stringValue2 = 'leavesC =_=';
var stringValue3 = "分段 "
"換行了也可以"
'又換了一行';
print(stringValue3); //分段 換行了也可以又換了一行
此外小作,也可以使用帶有單引號或雙引號的三重引號亭姥,此時包含的轉義字符是有效的
var stringValue='''\n 換行符
\t 制表符
''';
也可以用 r前綴 創(chuàng)建一個原始字符串,包含的轉義字符將會失效
var stringValue=r'''
\n 換行符
\t 制表符
''';
3顾稀、bool
dart 語言用 bool 關鍵字來表示真假兩面达罗,bool 類型只有兩個實例:true 和 false,它們都是編譯時常量静秆,且只有 bool 類型的值是 true 才被認為是 true
4粮揉、list
list 也是最常見的數(shù)據(jù)類型之一,dart 通過方括號來聲明 list 變量
由于 dart 具有類型推導功能抚笔,因此 listValue 自動被賦予為 List< int > 類型扶认,因此在聲明 listValue 后就無法為其添加其他類型變量的值了
var listValue = [1, 2, 3];
// listValue.add("4"); error
print(listValue.runtimeType); //List<int>
如果想要為 List 添加不同數(shù)據(jù)類型的變量,則需要直接指明數(shù)據(jù)類型為 Object
var listValue = <Object>[1, 2, 3];
listValue.add("4");
print(listValue.runtimeType); //List<Object>
大多數(shù)時候為了限制 List 的可存儲數(shù)據(jù)類型殊橙,在使用時就直接指明數(shù)據(jù)類型
var intList = <int>[1, 2, 3, 4];
如果在聲明 List 時調用了其指定集合大小的構造函數(shù)辐宾,則集合大小就是固定的了,列表的長度不能在運行時更改
var list = List<int>(2);
list.add(2); //error膨蛮,會導致拋出 Unsupported operation: Cannot add to a fixed-length list
要創(chuàng)建一個編譯時常量列表叠纹,則在列表字面量之前添加 const 關鍵字
var constantList = const [1, 2, 3];
//error,可正常編譯敞葛,但會導致運行時拋出異常
//constantList[0] = 2;
//constantList.add(2);
5誉察、set
Set 是一種不包含重復數(shù)據(jù)的數(shù)據(jù)集合,使用方式上和 List 基本類似
void main() {
var list = [1, 2, 2, 3, 4, 5, 5];
var set = Set.from(list);
print(set); //{1, 2, 3, 4, 5}
}
6惹谐、map
map 是一個關聯(lián)鍵和值的數(shù)據(jù)類型持偏,鍵和值可以是任何類型的對象
void main() {
var mapValue = {"name": "leavesC", "age": 24};
mapValue["url"] = "https://github.com/leavesCZY";
print(mapValue); //{name: leavesC, age: 24, url: https://github.com/leavesCZY}
print(mapValue.length); //3
print(mapValue.runtimeType); //_InternalLinkedHashMap<String, Object>
}
也可以限定 map 可以存儲的數(shù)據(jù)類型
var mapValue = <String, String>{"name": "leavesC"};
與 list 類似,要創(chuàng)建一個編譯時常量的 map 需要在 map 的字面量前加上 const 關鍵字
var mapValue = const {"name": "leavesC", "age": 24};
//error豺鼻,可正常編譯综液,但會導致運行時拋出異常
mapValue["name"] = "hi";
四、函數(shù)
dart 是一種真正的面向對象語言儒飒,所以即使函數(shù)也是對象谬莹,即變量可以指向函數(shù),也可以將函數(shù)作為參數(shù)傳遞給其他函數(shù)
1、一般概念
一般附帽,為了方便調用者理解埠戳,函數(shù)需要指明其接受的參數(shù)類型,但也允許不指明參數(shù)類型蕉扮,此時函數(shù)依然可以正常調用
void main() {
printMsg("leavesC");
printMsg2(100);
printMsg2("leavesC");
}
void printMsg(String msg) {
print(msg);
}
void printMsg2(msg) {
print(msg);
}
如果函數(shù)只包含一個表達式整胃,則可以使用簡寫語法
void printMsg3(msg) => print(msg);
所有函數(shù)均有返回值,如果沒有指明函數(shù)的返回值類型喳钟,則函數(shù)默認返回 null
void main() {
print(printValue(121) == null); //true
}
printValue(value) {
print("value is: $value");
}
2屁使、函數(shù)也是對象
在 dart 中,可以用變量來引用函數(shù)對象奔则、向函數(shù)傳遞函數(shù)參數(shù)蛮寂、創(chuàng)建函數(shù)對象
void main() {
var printUserFun = printName;
printUserFun("leavesC"); //name: leavesC
var list = ["leavesC", "葉"];
list.forEach(printName); //name: leavesC name: 葉
var sayHelloFun = (String name) => print("$name , hello");
sayHelloFun("leavesC"); //leavesC , hello
}
void printName(String name) {
print("name: $name");
}
3、位置參數(shù)
位置參數(shù)即該參數(shù)可傳也可不傳易茬,當不傳時該參數(shù)值默認為 null酬蹋,位置參數(shù)用中括號包裹起來
void main() {
printUser("leavesC"); //name: leavesC, age: null
printUser("leavesC", 25); //name: leavesC, age: 25
}
void printUser(String name, [int age]) {
print("name: $name, age: $age");
}
4、命名參數(shù)
命名參數(shù)抽莱,即在調用該函數(shù)時需同時標明該參數(shù)的參數(shù)名范抓,命名參數(shù)用花括號包裹起來,以 {type paramName}
或者 {paramName: type}
兩種方式聲明參數(shù)食铐,調用命名參數(shù)時只能以 funcName(paramName: paramValue)
的形式來調用匕垫。且命名參數(shù)可傳也可不傳值,當不傳指時該參數(shù)值為 null
void main() {
printUser("leavesC"); //name: leavesC, age: null
printUser("leavesC", age: 25); //name: leavesC, age: 25
printUser2("leavesC", age: 25); //name: leavesC, age: 25
}
void printUser(String name, {int age}) {
print("name: $name, age: $age");
}
void printUser2(String name, {age: int}) {
print("name: $name, age: $age");
}
5虐呻、默認參數(shù)值
和 kotlin 類似年缎,dart 語言也支持為位置函數(shù)和命名參數(shù)設置默認值,默認值必須是編譯時常量铃慷,如果沒有提供默認值,則默認值為 null
void main() {
printUser("leavesC"); //name: leavesC, age: 30
printUser("leavesC", 25); //name: leavesC, age: 25
printUser2("leavesC"); //name: leavesC, age: 30
printUser2("leavesC", age: 25); //name: leavesC, age: 25
}
void printUser(String name, [int age = 30]) {
print("name: $name, age: $age");
}
void printUser2(String name, {int age = 30}) {
print("name: $name, age: $age");
}
6蜕该、函數(shù)變量
前面說了犁柜,dart 是一種真正的面向對象語言,即使函數(shù)也是對象堂淡,即變量可以賦予函數(shù)類型
void main() {
var printFunction = printUser;
printFunction("leavesC");
print(printFunction); //Closure: (String, [int]) => void from Function 'printUser': static.
}
void printUser(String name, [int age = 30]) {
print("name: $name, age: $age");
}
也可以將函數(shù)作為參數(shù)傳遞給另外一個函數(shù)
void main() {
var list = {1, 2, 3};
// value is: 1
// value is: 2
// value is: 3
list.forEach(printValue);
}
void printValue(value) {
print("value is: $value");
}
7馋缅、匿名函數(shù)
匿名函數(shù)即不具備函數(shù)名稱的函數(shù),在函數(shù)只使用一次會就不再調用時使用匿名函數(shù)會比較方便
void main() {
var list = {1, 2, 3};
// value is: 1
// value is: 2
// value is: 3
list.forEach((element) {
print("value is: $element");
});
list.forEach((element) => print("value is: $element"));
}
8绢淀、局部函數(shù)
局部函數(shù)即嵌套于其他函數(shù)內部的函數(shù)
void main() {
var list = {1, 2, 3};
// value is: 2
// value is: 3
// value is: 4
list.forEach((element) {
int add(int value1, int value2) {
return value1 + value2;
}
print("value is: ${add(element, 1)}");
});
}
五萤悴、運算符
dart 提供了一些比較簡便的運算符來簡化操作,大部分和 Java 相同皆的,以下介紹下幾個不同的運算符
void main() {
//is 用于判斷變量是否是指定的數(shù)據(jù)類型
//is! 含義是 is 取反
var strValue = "leavesC";
print(strValue is String); //true
print(strValue is int); //false
print(strValue is! String); //false
// ~/ 用于除法運算時取整覆履,/ 則不取整
print(10 / 3); //3.3333333333333335
print(10 ~/ 3); //3
//as 用于強制類型轉換
num numValue = 10;
int intValue = numValue as int;
//如果 ??= 左邊的變量值為 null ,則將其賦值為右邊的值
var name = null;
var age = 25;
name ??= "leavesC";
age ??= 30;
print("name:$name"); //name:leavesC
print("age: $age"); //age: 25
//如果 ?. 左邊的變量值不為 null,則右邊的操作生效
//用于避免發(fā)生空指針異常
var area = null;
print(area?.runtimeType); //null
//級聯(lián)運算符 .. 用于連續(xù)操作某個對象,而無需每次操作時都調用該對象
var list = [1, 2, 3, 4, 5];
list
..insert(0, 6)
..removeAt(4)
..add(7);
print(list); //[6, 1, 2, 3, 5, 7]
//擴展運算符 ... 和 空值感知擴展運算符 ...?
//提供了一種將多個元素插入集合的簡潔方法
var list1 = [1, 2, 3];
var list2 = [0, ...list1];
print(list2); //[0, 1, 2, 3]
//如果 list3 可能為 null硝全,此時則需要使用空值感知擴展運算符栖雾,否則會拋出異常
//空值感知擴展運算符只有當 list3 不為 null 時才會執(zhí)行插值操作
var list3 = null;
var list4 = [0, ...?list3];
print(list4); //[0]
}
六、流程控制
dart 的流程控制的語義和邏輯大多和 Java 相同
void main() {
//if
int value = 20;
if (value < 10) {
print("value < 10");
} else if (value < 30) {
print("value < 30");
} else {
print("value unknown");
}
//while
int score = 10;
while (score < 100) {
score++;
}
//switch
String strValue = "leavesC";
switch (strValue) {
case "ye":
break;
case "leavesC":
print(strValue);
break;
default:
print("default");
break;
}
//for
var list = [];
for (int index = 1; index < 10; index++) {
list.add(index.toString());
}
for (var item in list) {
print("循環(huán)遍歷:$item");
}
}
此外也有一些比較奇特的操作伟众,可以通過條件 if 和循環(huán) for 來構建集合
var hasName = true;
var info = {if (hasName) "name": "leavesC", "age": 24};
print(info); //{name: leavesC, age: 24}
var list = {1, 2, 3};
var info = {for (var item in list) "$item", 4};
print(info); //{1, 2, 3, 4}
七析藕、枚舉
枚舉用于定義命名常量值,使用enum
關鍵字來聲明枚舉類型
enum State { RESUME, STOP, PAUSE }
void main() {
var state = State.PAUSE;
print(state);
State.values.forEach((state) {
print(state);
});
}
八凳厢、異常處理
dart 語言可以拋出和捕獲異账胧,捕獲異常可以避免程序運行崩潰先紫,與 Java 不同治泥,dart 的所有異常均是未檢查異常,方法不必聲明本身可能拋出的異常泡孩,也不要求調用方捕獲任何異常车摄。dart 提供了 Exception 和 Error 類型,以及許多預定義的子類型仑鸥,開發(fā)者也可以自己定義異常吮播。而且,dart 可以拋出任何對象眼俊,不僅僅是 Exception 和 Error 兩類
例如意狠,如下代碼就表示 throwException
方法內部捕獲了 RangeError ,但對其他異常不進行處理
void throwException() {
try {
List<String> stringList = new List();
stringList.add("value");
print('${stringList[1]}');
} on RangeError {
print("拋出了異常...");
}
}
void main() {
throwException();
}
如果在拋出異常時需要異常對象疮胖,則需要用到 catch
void throwException() {
try {
List<String> stringList = new List();
stringList.add("value");
print('${stringList[1]}');
} on RangeError catch (e) {
print("${e.message}"); //Invalid value
print("${e.runtimeType}"); //RangeError
} catch (e) {
print("如果異常沒有被上方捕獲环戈,則會統(tǒng)一被此處捕獲");
}
}
也可以使用兩個參數(shù)的寫法,第二個參數(shù)代表堆棧跟蹤(StackTrace對象)
void throwException() {
try {
List<String> stringList = new List();
stringList.add("value");
print('${stringList[1]}');
} on RangeError catch (e, s) {
print("${s.toString()}");
}
}
也可以在捕獲異常后將異常再次拋出
void throwException() {
try {
List<String> stringList = new List();
stringList.add("value");
print('${stringList[1]}');
} on RangeError catch (e, s) {
print("${s.toString()}");
rethrow;
}
}
類似 Java澎灸,finally
用于確保在拋出異常時某些代碼也可以被執(zhí)行
void throwException() {
try {
List<String> stringList = new List();
stringList.add("value");
print('${stringList[1]}');
} on RangeError catch (e, s) {
print("throwException ${e.message}");
rethrow;
} finally {
print("finally");
}
}
void main() {
try {
throwException();
} catch (e) {
print("main ${e.message}");
}
// throwException Invalid value
// finally
// main Invalid value
}
此外院塞,dart 也允許 throw 任何對象,包括 null
void throwException() {
try {
List<String> stringList = new List();
stringList.add("value");
print('${stringList[1]}');
} on RangeError catch (e, s) {
throw null;
//or throw "發(fā)生了異常";
}
}
void main() {
try {
throwException();
} catch (e) {
print("main ${e}"); //main Throw of null.
print("${e.runtimeType}"); //NullThrownError
}
}
此外性昭,由于拋出異常是一個表達式拦止,所以以下寫法是合乎語法的
int throwException() => throw "";
九、類
1糜颠、類聲明
dart 是一門完全面向對象的語言汹族,其關于類的內容和 Java 較為類似
class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
}
dart 會自動為 name
和 age
提供隱式的 getter
和 setter
方法,且未經(jīng)初始化的實例變量均為 null
在 dart 2 中 new 關鍵字成為了可選關鍵字其兴,因此可以選擇省略 new 聲明顶瞒,這一點和 kotlin 相同
void main() {
var person = Person("leavesC", 25);
print('${person.name} ${person.age}'); //leavesC 25
print('${person.runtimeType}'); //Person
}
2、構造函數(shù)
dart 為用于賦予初始值的構造函數(shù)提供了簡便寫法元旬,以下兩種寫法的語義是一致的
Person(String name, int age) {
this.name = name;
this.age = age;
}
Person(this.name, this.age);
此外榴徐,dart 也提供了命名構造函數(shù)守问,用于方便調用者生成不同用途或含義的變量
Person.getDefault() {
this.name = "leavesC";
this.age = 25;
}
也可以在構造函數(shù)主體運行之前初始化實例變量
Person.getDefault()
: name = "leavesC",
age = 25 {}
因此當想要獲取一個默認的 Person
實例時就如同在調用 Person 的一個靜態(tài)方法
void main() {
var defaultPerson = Person.getDefault();
print('${defaultPerson.name} ${defaultPerson.age}'); //leavesC 25
print('${defaultPerson.runtimeType}'); //Person
}
3、繼承
默認情況下箕速,子類中的構造函數(shù)會隱式調用父類的未命名的無參數(shù)構造函數(shù)酪碘,父類的構造函數(shù)在子類構造函數(shù)體的開始處被調用。如果父類沒有未命名的無參數(shù)構造函數(shù)盐茎,則必須手動調用父類中的構造函數(shù)
此外兴垦,構造函數(shù)不能被子類繼承,父類中的命名構造函數(shù)不會被子類繼承字柠,所以如果子類也想要擁有一個和父類一樣名字的構造函數(shù)探越,則必須子類自己實現(xiàn)這個構造函數(shù)
class Man extends Person {
Man(String name, int age) : super(name, age);
Man.getDefault() : super.getDefault();
}
4、抽象類
dart 語言的抽象類和 Java 基本一致
abstract class Printer {
void print(String msg);
}
class HpPrinter extends Printer {
@override
void print(String msg) {
// TODO: implement print
}
}
5窑业、接口
dart 沒有用來專門聲明接口的語法钦幔,類聲明本身就是 dart 中的接口,實現(xiàn)類使用implements
關鍵字來實現(xiàn)接口常柄,實現(xiàn)類必須提供目標接口的所有功能的具體實現(xiàn)鲤氢,即類必須重新定義它希望實現(xiàn)的接口中的每一個函數(shù)
void main() {
var human = Human();
human.eat();
human.speak();
}
class Human implements Eat, Speak {
void funA() {}
@override
void eat() {
// TODO: implement funB
}
@override
void speak() {
// TODO: implement funC
}
}
class Eat {
void eat() {}
}
class Speak {
void speak() {}
}
6、mixins
mixins 是一個重復使用類中代碼的方式
void main() {
var c = C();
c.funA();
c.funB();
}
class A {
void funA() {}
}
class B {
void funB() {}
}
//使用 with 關鍵字表示類C是由類A和類B混合構成的
class C = A with B;