[TOC]
定位
首先本文是通過對(duì)比其他語(yǔ)言(主要是java)快速入門,建議您至少要熟悉一門靜態(tài)語(yǔ)言或者動(dòng)態(tài)語(yǔ)言。Dart和java以及C#都差不多,基本上不用學(xué)習(xí)可以直接使用,從這里可以你可以了解Dart有些特別之處。
首先從一段代碼入門吧:
void main() {
for (int i = 0; i < 5; i++) {
printTest('hello ${i + 1}');
}
}
void printTest(String variableInteger) {
print(variableInteger);
}
從上面可以看到,Dart語(yǔ)言主入口是main函數(shù)
,他可以不用屬于某個(gè)類。一眼看上去和java差不多。流程控制也是由小括號(hào)和大括號(hào)構(gòu)成,不用考慮縮進(jìn)。結(jié)尾必須要加上分號(hào)。
聲明數(shù)據(jù)類型
void main() {
int l = 4;
String str1 = "str1";
var i = 1;
dynamic d = 0;
final j = 2;
const k = 3;
}
Dart是強(qiáng)類型語(yǔ)言,變量都會(huì)有一個(gè)類型。你可以向java那樣直接聲明類型,同樣可以像swift或者Kotlin那樣使用類型推導(dǎo)。Dart的類型推導(dǎo)使用final,var,const,dynamic關(guān)鍵字。
-
var
修飾變量,這個(gè)沒什么好說的。 -
final
表示不可變的,修飾內(nèi)置數(shù)據(jù)類型,值不可變;修飾對(duì)象表示引用不可變,使用到的頻率很高。 -
const
是編譯時(shí)常量,他表示始終不可變,無(wú)論修飾內(nèi)置類型還是對(duì)象,或者是數(shù)據(jù)結(jié)構(gòu)。 -
dynamic
是任意類型,有點(diǎn)像java里面的Object
,Kotlin中的Any
。
使用const修飾的都是編譯時(shí)常量。它區(qū)別于運(yùn)行時(shí),它的值是不可變的。
所有的數(shù)據(jù)類型,不管是int還是String,初始值都是null
數(shù)據(jù)類型
numbers、strings和booleans
首先是numbers
,strings
,booleans
其中numbers包括int
和double
類型,分別表示整數(shù)和浮點(diǎn)數(shù)。strings就是String
類型,普通的字符串。booleans類型是bool
只包含true
和false
。
numbers
* int
* double
strings
* String
booleans
* bool
如下代碼:
void main() {
// numbers
int i = 1;
double d = 1.1;
// 字符串轉(zhuǎn)數(shù)字
// 這里就使用了類型推導(dǎo)
final p1 = "3";
final p2 = "3.3";
print(int.parse(p1));
print(double.parse(p2));
// string
// 可以使用('xxx')("xxx")('''xxx''') 三種方式初始化字符串,和Kotlin很像。
// 字符串比較的是值
final str1 = "test";
final str2 = "test";
print(str1 == str2); // true
final str3 = 'test';
print(str3);
final str4 = '''test''';
print(str4);
print(str4 == str3); // true
print("I'm ${str4}!!!");
//booleans
// null 可以直接用來判斷,它是false
if(null) {
print('null is true');
} else {
print('null is false');
}
print(''.isEmpty);
// final list = [];
// if(!list) {
// print('not empty');
// }
}
Dart的數(shù)據(jù)結(jié)構(gòu)包括list和map
list
list
對(duì)應(yīng)的就是java里面的List,list可以像Python等語(yǔ)言那樣使用[]
進(jìn)行數(shù)組操作,參照以下示例代碼。
List有幾個(gè)在flutter非常常用的初始化方式,分別為filled
和generate
。具體參考:https://api.dartlang.org/stable/2.1.0/dart-core/List-class.html
void main() {
// 初始化
final list1 = [1,2,3,4,5];
// 編譯時(shí)常量
final list2 = const [2,3,4,5,6];
// list2[2] = 8; error
print(list2);
// 初始化2
List<int> list3 = new List(5);
print(list3); // [null, null, null, null, null]
// 初始化3 filled 第一個(gè)參數(shù)是個(gè)數(shù),第二個(gè)是添加的值
List<int> list4 = List.filled(3,10);
print(list4); // [10,10,10]
// 初始化4 類似Python的Map生成
List<int> list5 = List.generate(3,(i) => i * 3);
print(list5); // [0, 3, 6]
// 更改 使用list1
list1[2] = 8;
print(list1); // [1, 2, 8, 4, 5]
// 添加
list1.add(10);
// list1[10] = 20; Index out of range
print(list1); // [1, 2, 8, 4, 5, 10]
//刪除
list1.remove(10);
print(list1); // [1, 2, 8, 4, 5]
}
map
map
對(duì)應(yīng)java里面的Map,他的操作方式有點(diǎn)像Python。需要注意的有幾點(diǎn)。
- Map的Key不能有重復(fù)的
- 如果使用int類型作為key,取值的時(shí)候優(yōu)先使用key匹配,然后是下標(biāo)。
- 取不到值不會(huì)拋異常,直接返回null
- Map和List可以轉(zhuǎn)換為Json,來回轉(zhuǎn)換時(shí)深拷貝。
其他API參考:https://api.dartlang.org/stable/2.1.0/dart-core/Map-class.html
import 'dart:convert';
void main() {
final map1 = {
"test1key" : "test1value",
// "test1key" : "test1value", Key 不能相同
123 : "123value",
null : 'NULL',
2 : '2'
};
print(map1); // {test1key: test1value, 123: 123value, null: NULL, 2: 2}
// 獲取值
print(map1[123]); // key取值 123value
print(map1[1]); // 下標(biāo)取值 null
print(map1[2]); // 同時(shí)存在時(shí)候,優(yōu)先Key取值;結(jié)果為 2
// 追加值
map1['appendKey'] = 'appendVal';
print(map1);
// 更改值
map1['appendKey'] = 'appendVal123';
print(map1);
map1['appendKey'] = null;
print(map1); // 置為null之后,key不會(huì)消失
map1.remove('appendKey');
// 刪除
print(map1);
print(map1['non-key']); // 不會(huì)報(bào)錯(cuò) 打印 null
print(map1.length); // 長(zhǎng)度 4
// json 轉(zhuǎn)換
// 使用const修飾的數(shù)據(jù)結(jié)構(gòu),不可改變。
final map2 = const {
"name":"lecon",
"age":23,
"ss":[1,2,3,4,4]
};
final jsonText = jsonEncode(map2);
print(jsonText); // {"name":"lecon","age":23,"ss":[1,2,3,4,4]}
final mapObj = jsonDecode(jsonText);
print(mapObj); // {name: lecon, age: 23, ss: [1, 2, 3, 4, 4]}
}
Runes
這東西其實(shí)就是字符集的擴(kuò)編,可以用它顯示一些表情符號(hào)
main() {
var clapping = '\u{1f44f}';
print(clapping);
print(clapping.codeUnits);
print(clapping.runes.toList());
Runes input = new Runes(
'\u2665 \u{1f605} \u{1f60e} \u{1f47b} \u{1f596} \u{1f44d}');
print(new String.fromCharCodes(input));
}
函數(shù)
Dart的函數(shù)比java多了幾個(gè)概念。比如在參數(shù)列表里面添加個(gè)大括號(hào),是可選命名參數(shù)
;加個(gè)中括號(hào)叫可選位置參數(shù)
。
- 可選命名參數(shù)類似Python里面的**kwargs,參考下面示例代碼
func3
; - 可選位置參數(shù)類似Python里面的*args,參考下面
func5
。 - 可選命名參數(shù)和可選位置參數(shù)可以有默認(rèn)值,普通參數(shù)不能有,參考
func6
。 - 函數(shù)之間可以互相嵌套,互相調(diào)用,但他們都有自己作用域。
- 若果函數(shù)簡(jiǎn)單可以使用
()=> value
簡(jiǎn)寫。
比如
String test() {
return "123";
}
簡(jiǎn)寫成
String test ()=> "123";
下面是示例代碼
// import 'package:meta/meta.dart'
void main() {
func1(123);
func2('222');
func3("lecon");
// func3("lecon","spawn",123); error,使用下邊的中括號(hào),注意區(qū)別。這個(gè)使用key區(qū)分,下面使用位置區(qū)分
func3("lecon",param2:"spawn",param3:123);
// func4("lecon",param2:"spawn"); error required
func5("lecon","spawn","hahaa");
func6("lecon",age:25);
print(func7());
}
void func1(param1) {
print(param1);
}
void func2(String param2) {
print(param2);
}
void func3(String param1,{String param2,int param3}) {
print("I am ${param1}, Hi ${param2},I am ${param3}");
}
// test in flutter
// void func4(String param1,{String param2,@required int param3}) {
// print("I am ${param1}, Hi ${param2},I am ${param3}");
// }
void func5(String param1,[String param2,String param3]) {
print("I am ${param1}, bian bu ${param2} xia qu le ${param3}");
}
// 只有使用{}和[]的才可以有默認(rèn)值
void func6(String param1,{int age = 23}) {
print("I am ${param1}, I am ${age}");
}
func7() {
void func8() {
}
void func9() {
}
retrun () => "123";
}
流程控制
Dart的流程控制和java的用法基本一樣,這里就不介紹了。
有個(gè)for in要說下,和JavaScript很像。
for (var x in collection) {}
相當(dāng)于java的高級(jí)for循環(huán)
for(Integer i : indexs) {}
面向?qū)ο?/h3>
相對(duì)于java有這幾點(diǎn)需要注意
-
new
關(guān)鍵字可以省略 - 一個(gè)類同名構(gòu)造器只能有一個(gè),也就是說可以給構(gòu)造器起其他名字。所以可以把構(gòu)造器分為
默認(rèn)構(gòu)造器
和命名構(gòu)造器
。 - 命名構(gòu)造器不能自動(dòng)繼承,參考下面
- Dart使用
this
代表當(dāng)前對(duì)象 - 初始化列表
- Dart有繼承和接口概念,和java基本差不多,同時(shí)多了個(gè)
混入
的的概念minix
,這個(gè)非常好理解。
下面這個(gè)例子是類構(gòu)造器的基本使用
普通構(gòu)造器和繼承
class Person {
String name;
int age;
String dantengt;
// 注意,冒號(hào)后邊是初始化列表,用,隔開。
Person() :dantengt = "danm";
@override
String toString() => "name : ${name};age : ${age}";
}
class Person2 {
String name;
int age;
// 默認(rèn)構(gòu)造器只能聲明一個(gè)
// 不用寫set方法,使用this可以直接把值賦給相應(yīng)的屬性
Person2(this.name,this.age);
// 命名構(gòu)造器
Person2.copy(Person p) {
this.name = p.name;
this.age = p.age;
}
@override
String toString() => "name : ${name};age : ${age}";
}
class Student extends Person2 {
final String school;
// 普通構(gòu)造器繼承
Student(String name,int age,this.school):super(name,age);
// 命名構(gòu)造器的繼承
Student.copy(Person p,this.school):super.copy(p);
// 類似構(gòu)造器的重載
Student.mySchoolCopy(Person p):this.copy(p,'mySchool');
}
// 所有值都不可以改變
class ImmutablePoint {
static final ImmutablePoint origin =
const ImmutablePoint(0, 0);
final num x, y;
// cosnt修飾的構(gòu)造器是常量構(gòu)造器,里面的值都不許改變。因?yàn)槭蔷幾g時(shí)的。
const ImmutablePoint(this.x, this.y);
}
void main() {
final person = Person();
person.name = "spawn";
person.age = 18;
print(person);
final person2 = Person2("lecon",25);
print(person2);
print(Person2.copy(person));
Person2 p2 = Student("lecon",25,"haha");
// is 和 as ;類似 instanceof和強(qiáng)轉(zhuǎn)
if (p2 is Student) {
(p2 as Student).school;
}
}
其中:
- Person類的構(gòu)造器后面有個(gè)
:
,那個(gè)就是初始化列表,可以給屬性賦值(參考Person類),還可調(diào)用父類構(gòu)造器。 - Person2 類有兩個(gè)構(gòu)造器,默認(rèn)構(gòu)造器沒有名字,和java中一樣使用類名聲明。另外還有一個(gè)
Person2.copy()
他也是一個(gè)構(gòu)造器,只不過它有名字了,同名構(gòu)造器只能出現(xiàn)一次,不管是不是重載(貌似Dart沒重載)。 - 在構(gòu)造器參數(shù)列表中,如果直接使用
this
關(guān)鍵字,可以直接把值付給類的屬性,省去了set方法(參考類Person2的默認(rèn)構(gòu)造器)。 - 在類中,所有的屬性或者方法,只要帶了
_
前綴,那么他就是私有的,Dart文件之外不能訪問,當(dāng)前文件不同類是可以的。 -
is
關(guān)鍵字相當(dāng)于instanceof
,as
相當(dāng)于強(qiáng)轉(zhuǎn)。這個(gè)和Kotlin很像。 -
const
修飾構(gòu)造器,代表類是編譯時(shí)的。所有內(nèi)容不允許改變。 - 類的繼承使用
extends
關(guān)鍵字,同時(shí)dart也有接口
,和java基本一致,不再贅述。 - 除了繼承和接口之外,Dart還有個(gè)
mixin
混入的概念,可以把他理解為:把其他類的東西,一股腦放到當(dāng)前類中,使用with
關(guān)鍵字描述。
minix
關(guān)于minix參考以下代碼:
// class關(guān)鍵字換成minix,其他和class一樣,只是換個(gè)名字。
mixin Play {
void play() {
print("I can play");
}
}
mixin Eat {
void eat() {
print("I can eat");
}
}
// 使用with關(guān)鍵字加入mixin類
class Person with Play,Eat {
final String name;
final int age;
Person(this.name,this.age);
}
void main() {
final p = Person("lecon",13);
// 此時(shí)p有了Eat和Play的能力
p.play();
p.eat();
}
factory構(gòu)造器和多態(tài)
java編程中,在使用多態(tài)的時(shí)候,我們可能會(huì)用到工廠方法,dart給我們提供一個(gè)方便,使用dart的工廠構(gòu)造器。dart的工廠構(gòu)造器也是個(gè)構(gòu)造器,只不過使用factory修飾,他返回當(dāng)前類,并且不能使用this關(guān)鍵字,示例代碼。
class Person2 {
String name;
int age;
Person2(this.name,this.age);
// factory 構(gòu)造器也是構(gòu)造器,同樣不能聲明兩個(gè)相同名字的,而且只能有一個(gè)默認(rèn)
// factory 沒有this引用
factory Person2.select(name,int age,int type) {
if(type == 0) {
return Student(name,age,"jaja");
} else {
return Worker(name,age,"lala");
}
}
// callable
call(String a, String b, String c) => '$a $b $c!';
@override
String toString() => "name : ${name};age : ${age}";
}
class Student extends Person2 {
final String school;
Student(String name,int age,this.school):super(name,age);
}
class Worker extends Person2 {
final String company;
Worker(String name,int age,this.company):super(name,age);
}
void main() {
// 不關(guān)注Person2實(shí)現(xiàn)類,直接使用Person2實(shí)例化。使用factory
final p1 = Person2.select("lecon",23,0);
final p2 = Person2.select("lecon",23,1);
print(p1.runtimeType); // Student
print(p2.runtimeType); // Worker
// callable
print(p1("123","234","345"));
}
其中select
為Person2的工廠構(gòu)造器,由它來返回對(duì)應(yīng)的實(shí)現(xiàn)類。
setter和getter
很多語(yǔ)言里面,為了方便獲取屬性都提供了setter和getter。其實(shí)他就是個(gè)語(yǔ)法糖。直接使用就可以。看例子:
class Rectangle {
num left, top, width, height;
Rectangle(this.left, this.top, this.width, this.height);
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);
}
異常
Dart的異常也和java非常像。直接看例子
void main() {
// throw1();
// throw2();
// throw3();
// throw4();
throw5();
}
void throw1() {
throw FormatException('Expected at least 1 section');
}
void throw2() {
throw 'Expected at least 1 section';
}
void throw3() {
try {
throw FormatException('Expected at least 1 section');
} on FormatException {
print('section exception');
}
try {
throw FormatException('Expected at least 1 section');
} on FormatException catch(e) {
print('section exception ${e}');
}
try {
throw FormatException('Expected at least 1 section');
} on FormatException catch(e,s) {
print('section exception ${e}');
print(s); // 打印方法調(diào)用棧
}
}
void throw4() {
try {
throw FormatException('Expected at least 1 section');
} on FormatException {
print('section exception');
rethrow; // 重拋
}
}
void throw5() {
try {
throw FormatException('Expected at least 1 section');
} on FormatException {
print('section exception');
} finally {
print("I am finally");
}
}
- dart可以直接拋出默認(rèn)異常,
throw 'Expected at least 1 section';
- 使用
on
來匹配異常類型,on FormatException
- 使用
catch
來獲取異常變量,catch(e,s)
它有一個(gè)默認(rèn)參數(shù)和可選參數(shù)。分別為信息和調(diào)用棧。 - 使用
rethrow
重新拋出異常
異步
dart的異步和JavaScript ES6差不多,使用await
和async
,只不過ES6中的Promise變成了Future。目前比較流行的解決方案都是這個(gè),比如Python,Kotlin都這么使用。一般在網(wǎng)絡(luò)請(qǐng)求或者數(shù)據(jù)庫(kù)操作時(shí)候使用,就像java的多線程基礎(chǔ)版吧。
異步這塊東西有點(diǎn)多,而且很重要,直接貼官方地址。https://www.dartlang.org/guides/libraries/library-tour#future
異步中有兩個(gè)很重要的接口:Future
和Stream
。貼出地址,這兩個(gè)一定要弄明白。https://www.dartlang.org/guides/libraries/library-tour#dartasync---asynchronous-programming
生成器
Dart的生成成器和其他語(yǔ)言里面的生成器差不多,分為同步
和異步
兩種。同步生成器結(jié)構(gòu)是Iterable
,異步生成器接口是Stream
。流程控住使用async*
(異步)和sync*
(同步)聲明,使用yield
流程控制,每一次yield
都會(huì)給列表生成一個(gè)值,也就是說生成器返回結(jié)果的數(shù)據(jù)結(jié)構(gòu)可以當(dāng)列表使用。以下代碼:
void main() {
print(naturalsTo(10)); // (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
print(naturalsDownFrom(10)); // (10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
asynchronousNaturalsTo(10)
.toList() // toList把異步轉(zhuǎn)為同步
.then((res) {
print(res); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
});
}
// 同步生成器
Iterable<int> naturalsTo(int n) sync* {
int k = 0;
while (k < n) yield k++;
}
// 異步生成器
Stream<int> asynchronousNaturalsTo(int n) async* {
int k = 0;
while (k < n) yield k++;
}
// 遞歸性質(zhì)的生成器
Iterable<int> naturalsDownFrom(int n) sync* {
if (n > 0) {
yield n;
yield* naturalsDownFrom(n - 1);
}
}
其他
import關(guān)鍵字
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
// 僅導(dǎo)入foo
import 'package:lib1/lib1.dart' show foo;
// 排除foo
import 'package:lib2/lib2.dart' hide foo;
// 異步導(dǎo)入
import 'package:greetings/hello.dart' deferred as hello;
空判斷
Dart也有null
類型,并且和其他語(yǔ)言類似,Dart也是用?
來解決空問題。
void main() {
final a = null;
print(a != null ? a : "b"); // 等同于下邊,dart也支持三元運(yùn)算符
print(a ?? "b"); // 打印 b
String p = null;
print(p?.toString()); //打印null,而不會(huì)空指針
}
..
運(yùn)算符
Dart的聯(lián)級(jí)運(yùn)算符..
很有意思,很實(shí)用的語(yǔ)法糖,直接看代碼你就明白了。Builder
大法好。
void main() {
// 聯(lián)級(jí)運(yùn)算符
final p = Person()
..username = "lecon"
..password = "123456"
..setSex(true);
print(p); // Username: lecon; Password: 123456; Sex: true
}
class Person {
String _username;
String _password;
bool _sex;
set username(String username) => this._username = username;
set password(String password) => this._password = password;
void setSex(bool sex) => this._sex = sex;
@override
String toString() => "Username: ${this._username}; Password: ${this._password}; Sex: ${this._sex}";
}
Typedefs
這個(gè)和C++里邊的內(nèi)聯(lián)函數(shù)基本差不多。
官方例子 https://www.dartlang.org/guides/language/language-tour#typedefs
注釋
Dart注釋分為三種,單行注釋和多行注釋和java一致。doc注釋使用 ///
表示。
/// A domesticated South American camelid (Lama glama).
///
/// Andean cultures have used llamas as meat and pack
/// animals since pre-Hispanic times.
最后
臨時(shí)總結(jié),難免有錯(cuò)誤,請(qǐng)多多指出,我會(huì)在第一時(shí)間改正。
代碼地址
https://gist.github.com/leconio/73bd75eef9530ff76ac59e09b1331865