Dart 語言基礎入門篇


Dart 語言基礎入門篇


本文是【從零開始學習,開發個Flutter App】路上的第 1 篇文章。

這篇文章介紹了 Dart 的基礎特性,目的在於讓大家建立對 Dart 語言的總體認知,初步掌握 Dart 的語法。

我們假定讀者已經有一定的編程基礎,如果你瞭解 JavaScript 或者 Java 等面嚮對象語言,那 Dart 學習起來應該很有親切感。

Dart 是一門採取眾家之長的編程語言。儘管 Dart 很多語法和 JavaScript 很相似,但 Dart 語言同時是一門強類型的語言,它同時結合了像 Java 這樣強類型面嚮對象語言的特性,這使得它能勝任大型應用開發,同時它沒有 Java 的臃腫,Dart 語言在設計上非常簡潔、靈活和高效。

JavaScript 從簡單的瀏覽器腳本到服務端(nodejs),慢慢延伸到PC客戶端(electron)、App (React Native)甚至小程序開發,它已然成為一門真正意義上的全棧開發語言。

如果說 JavaScript 是在漫長的時光裡野蠻生長,那 Dart 從一開始就是精心設計出來的。如果說有一門語言取代JavaScript的位置,那很可能就是Dart。

Talk is cheep,下面就讓我們來親自感受一下這門語言的吧。

變量

你可以像 JavaScript 那樣聲明一個變量:

<code>var name = 'Bob';
/<code>

編譯器會推導出 name 的類型是String 類型,等價於:

<code>String name = 'Bob';
/<code>

我們可以從下面代碼窺見 Dart 是強類型語言的特性:

<code>var name = 'Bob';

// 調用 String 的方法
print(name.toLowerCase());

// 編譯錯誤
// name = 1;
/<code>

前面我們說過,Dart 除了具備簡潔的特點,而且也可以是非常靈活的,如果你想變換一個變量的類型,你也可以使用dynamic 來聲明變量,這就跟 JavaScript 一樣了:

<code>dynamic name = 'Bob'; //String 類型
name = 1;// int 類型
print(name);
/<code>

上面的代碼可以正常編譯和運行,但除非你有足夠的理由,請不要輕易使用。

final 的語義和 Java 的一樣,表示該變量是不可變的:

<code>// String 可以省略
final String name = 'Bob';

// 編譯錯誤
// name = 'Mary';
/<code>

其中 String 可以省略,Dart 編譯器足夠聰明地知道變量name 的類型。

如果要聲明常量,可以使用const 關鍵詞:

<code>const PI = '3.14';

class Person{
static const name = 'KK';
}

/<code>

如果類變量,則需要聲明為static const 。

內置類型

不像Java把類型分的特別細,比如整數類型,就有byte、short、int 、long 。Dart 的類型設計相當簡潔,這也是 Dart 容易上手的原因之一,可以理解為通過犧牲空間來換取效率吧。

數值類型

Dart 內置支持兩種數值類型,分別是int 和double ,它們的大小都是64位。

<code>var x = 1; 

// 0x開頭為16進制整數
var hex = 0xDEADBEEF;


var y = 1.1;
// 指數形式
var exponents = 1.42e5;
/<code>

需要注意的是,在Dart中,所有變量值都是一個對象,int和double類型也不例外,它們都是num類型的子類,這點和Java和JavaScript都不太一樣:

<code>// String -> int
var one = int.parse('1');
assert(one == 1);

// String -> double
var onePointOne = double.parse('1.1');
assert(onePointOne == 1.1);

// int -> String
String oneAsString = 1.toString();
assert(oneAsString == '1');

// double -> String
String piAsString = 3.14159.toStringAsFixed(2);
assert(piAsString == '3.14');
/<code>

字符串

Dart 字符串使用的是UTF-16編碼。

<code>var s = '中';
s.codeUnits.forEach((ch) => print(ch));
// 輸出為UNICODE值
20013
/<code>

Dart 採用了 JavaScript 中類似模板字符串的概念,可以在字符串通過${expression}語法插入變量:

<code>var s = "hello";

print('${s}, world!');

//可以簡化成:
print('$s, world!');

//調用方法
print('${s.toUpperCase()}, world!');
/<code>

Dart 可以直接通過==來比較字符串:

<code>var s1 = "hello";
var s2 = "HELLO";
assert(s1.toUpperCase() == s2);
/<code>

布爾類型

Dart 布爾類型對應為bool關鍵詞,它有true和false兩個值,這點和其他語言區別不大。值得一提的是,在Dart的條件語句if和assert表達式裡面,它們的值必須是bool類型,這點和 JavaScript 不同。

<code>var s = '';
assert(s.isEmpty);

if(s.isNotEmpty){
// do something
}

//編譯錯誤,在JavaScript常用來判斷undefined
if(s){
}
/<code>

Lists

你可以把Dart中的List對應到 JavaScript 的數組或者 Java 中的ArrayList,但 Dart 的設計更為精巧。

你可以通過類似 JavaScript 一樣聲明一個數組對象:

<code>var list = [];
list.add('Hello');
list.add(1);
/<code>

這裡List容器接受的類型是dynamic,你可以往裡面添加任何類型的對象,但如果像這樣聲明:

<code>var iList = [1,2,3];
iList.add(4);
//編譯錯誤 The argument type 'String' can't be assigned to the parameter type 'int'
//iList.add('Hello');
/<code>

那麼Dart就會推導出這個List是個List,從此這個List就只能接受int類型數據了,你也可以顯式聲明List的類型:

<code>var sList = List<string>();

//在Flutter類庫中,有許多這樣的變量聲明:
List<widget> children = const <widget>[];
/<widget>/<widget>/<string>/<code>

上面右邊那個 const 的意思表示常量數組,在這裡你可以理解為一個給children賦值了一個編譯期常量空數組,這樣的做法可以很好的節省內存,下面的例子可以讓大家更好的理解常量數組的概念:

<code>var constList = const [1,2];
constList[0] = 2; //編譯通過, 運行錯誤

constList.add(3); //編譯通過, 運行錯誤
/<code>

Dart2.3 增加了擴展運算符 (spread operator) ... 和...?,通過下面的例子你很容易就明白它們的用法:

<code>var list = [1, 2, 3];
var list2 = [0, ...list];
assert(list2.length == 4);
/<code>

如果擴展對象可能是null,可以使用...?:

<code> var list;
var list2 = [0, ...?list];
assert(list2.length == 1);
/<code>

你可以直接在元素內進行判斷,決定是否需要某個元素:

<code>var promoActive = true;
var nav = [
'Home',
'Furniture',
'Plants',
promoActive? 'About':'Outlet'
];
/<code>

甚至使用for來動態添加多個元素:

<code>var listOfInts = [1, 2, 3];
var listOfStrings = [
'#0',
for (var i in listOfInts) '#$i'
];
assert(listOfStrings[1] == '#1');
/<code>

這種動態的能力使得 Flutter 在構建 Widget 樹的時候非常方便。

Sets

Set的語意和其他語言的是一樣的,都是表示在容器中對象唯一。在Dart中,Set默認是LinkedHashSet實現,表示元素按添加先後順序排序。

<code>var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};
/<code>

遍歷Set,遍歷除了上面提到的for...in,你還可以使用類似 Java 的 lambada 中的 forEach 形式:

<code>halogens.add('bromine');
halogens.add('astatine');
halogens.forEach((el) => print(el));
/<code>

輸出結果:

<code>fluorine
chlorine
bromine
iodine
astatine
/<code>

除了容器的對象唯一特性之外,其他基本和List是差不多的。

<code>// 添加類型聲明:
var elements = <string>{};

var promoActive = true;
// 動態添加元素
final navSet = {'Home', 'Furniture', promoActive? 'About':'Outlet'};
/<string>/<code>

Maps

Map對象的聲明方式保持了 JavaScript 的習慣,Dart 中Map的默認實現是LinkedHashMap,表示元素按添加先後順序排序。

<code>var gifts = {
// Key: Value
'first': 'partridge',
'second': 'turtledoves',
'fifth': 'golden rings'
};

assert(gifts['first'] == 'partridge');
/<code>

添加一個鍵值對:

<code>gifts['fourth'] = 'calling birds';
/<code>

遍歷Map:

<code>gifts.forEach((key,value) => print('key: $key, value: $value'));
/<code>

函數

在 Dart 中,函數本身也是個對象,它對應的類型是Function,這意味著函數可以當做變量的值或者作為一個方法入傳參數值。

<code>void sayHello(var name){
print('hello, $name');
}

void callHello(Function func, var name){
func(name);
}

void main(){
// 函數變量
var helloFuc = sayHello;
// 調用函數
helloFuc('Girl');
// 函數參數
callHello(helloFuc,'Boy');
}

/<code>

輸出:

<code>hello, Girl
hello, Boy
/<code>

對於只有一個表達式的簡單函數,你還可以通過=>讓函數變得更加簡潔,=> expr在這裡相當於{ return expr; } ,我們來看一下下面的語句:

<code>String hello(var name ) => 'hello, $name';
/<code>

相當於:

<code>String hello(var name ){
return 'hello, $name';
}
/<code>

參數

在Flutter UI庫裡面,命名參數隨處可見,下面是一個使用了命名參數(Named parameters)的例子:

<code>void enableFlags({bool bold, bool hidden}) {...}
/<code>

調用這個函數:

<code>enableFlags(bold: false);
enableFlags(hidden: false);
enableFlags(bold: true, hidden: false);
/<code>

命名參數默認是可選的,如果你需要表達該參數必傳,可以使用@required:

<code>void enableFlags({bool bold, @required bool hidden}) {}
/<code>

當然,Dart 對於一般的函數形式也是支持的:

<code>void enableFlags(bool bold, bool hidden) {}
/<code>

和命名參數不一樣,這種形式的函數的參數默認是都是要傳的:

<code>enableFlags(false, true);
/<code>

你可以使用[]來增加非必填參數:

<code>void enableFlags(bool bold, bool hidden, [bool option]) {}
/<code>

另外,Dart 的函數還支持設置參數默認值:

<code>void enableFlags({bool bold = false, bool hidden = false}) {...}

String say(String from, [String device = 'carrier pigeon', String mood]) {}
/<code>

匿名函數

顧名思意,匿名函數的意思就是指沒有定義函數名的函數。你應該對此不陌生了,我們在遍歷List和Map的時候已經使用過了,通過匿名函數可以進一步精簡代碼:

<code>var list = ['apples', 'bananas', 'oranges'];
list.forEach((item) {
print('${list.indexOf(item)}: $item');
});
/<code>

閉包

Dart支持閉包。沒有接觸過JavaScript的同學可能對閉包(closure)比較陌生,這裡給大家簡單解釋一下閉包。

閉包的定義比較拗口,我們不去糾結它的具體定義,而是打算通過一個具體的例子去理解它:

<code>Function closureFunc() {
var name = "Flutter"; // name 是一個被 init 創建的局部變量
void displayName() { // displayName() 是內部函數,一個閉包
print(name); // 使用了父函數中聲明的變量
}
return displayName;
}

void main(){
//myFunc是一個displayName函數
var myFunc = closureFunc(); //(1)

// 執行displayName函數
myFunc(); // (2)
}
/<code>

結果如我們所料的那樣打印了Flutter。

在(1)執行完之後,name作為一個函數的局部變量,引用的對象不是應該被回收掉了嗎?但是當我們在內函數調用外部的name時,它依然可以神奇地被調用,這是為什麼呢?

這是因為Dart在運行內部函數時會形成閉包,閉包是由函數以及創建該函數的詞法環境組合而成,這個環境包含了這個閉包創建時所能訪問的所有局部變量

我們簡單變一下代碼:

<code>Function closureFunc() {
var name = "Flutter"; // name 是一個被 init 創建的局部變量
void displayName() { // displayName() 是內部函數,一個閉包
print(name); // 使用了父函數中聲明的變量
}
name = 'Dart'; //重新賦值
return displayName;
}
/<code>

結果輸出是Dart,可以看到內部函數訪問外部函數的變量時,是在同一個詞法環境中的。

返回值

在Dart中,所有的函數都必須有返回值,如果沒有的話,那將自動返回null:

<code>foo() {}

assert(foo() == null);
/<code>

流程控制

這部分和大部分語言都一樣,在這裡簡單過一下就行。

if-else

<code>if(hasHause && hasCar){
marry();
}else if(isHandsome){
date();
}else{

pass();
}
/<code>

循環

各種for:

<code>var list = [1,2,3];

for(var i = 0; i != list.length; i++){}

for(var i in list){}
/<code>

while和循環中斷(中斷也是在for中適用的):

<code>  var i = 0;
while(i != list.length){
if(i % 2 == 0){
continue;
}
print(list[i]);
}

i = 0;
do{
print(list[i]);
if(i == 5){
break;
}
}while(i != list.length);
/<code>

如果對象是Iterable類型,你還可以像Java的 lambada 表達式一樣:

<code>list.forEach((i) => print(i));

list.where((i) =>i % 2 == 0).forEach((i) => print(i));
/<code>

switch

switch可以用於int、double、String 和enum等類型,switch 只能在同類型對象中進行比較,進行比較的類不要覆蓋==運算符。

<code>var color = '';
switch(color){
case "RED":
break;
case "BLUE":
break;
default:

}
/<code>

assert

在Dart中,assert語句經常用來檢查參數,它的完整表示是:assert(condition, optionalMessage),如果condition為false,那麼將會拋出[AssertionError]異常,停止執行程序。

<code>assert(text != null);

assert(urlString.startsWith('https'), 'URL ($urlString) should start with "https".');
/<code>

assert 通常只用於開發階段,它在產品運行環境中通常會被忽略。在下面的場景中會打開assert:

  1. Flutter 的 debug mode。
  2. 一些開發工具比如dartdevc默認會開啟。
  3. 一些工具,像dart 和dart2js ,可以通過參數--enable-asserts 開啟。

異常處理

Dart 的異常處理和Java很像,但是Dart中所有的異常都是非檢查型異常(unchecked exception),也就是說,你不必像 Java 一樣,被強制需要處理異常。

Dart 提供了Exception 和 Error 兩種類型的異常。 一般情況下,你不應該對Error類型錯誤進行捕獲處理,而是儘量避免出現這類錯誤。

比如OutOfMemoryError、StackOverflowError、NoSuchMethodError等都屬於Error類型錯誤。

前面提到,因為 Dart 不像 Java 那樣可以聲明編譯期異常,這種做法可以讓代碼變得更簡潔,但是容易忽略掉異常的處理,所以我們在編碼的時候,在可能會有異常的地方要注意閱讀API文檔,另外自己寫的方法,如果有異常拋出,要在註釋處進行聲明。比如類庫中的File類其中一個方法註釋:

<code>  /**
* Synchronously read the entire file contents as a list of bytes.
*
* Throws a [FileSystemException] if the operation fails.
*/
Uint8List readAsBytesSync();
/<code>

拋出異常

<code>throw FormatException('Expected at least 1 section');
/<code>

throw除了可以拋出異常對象,它還可以拋出任意類型對象,但建議還是使用標準的異常類作為最佳實踐。

<code>throw 'Out of llamas!';
/<code>

捕獲異常

可以通過on 關鍵詞來指定異常類型:

<code> var file = File("1.txt");
try{
file.readAsStringSync();
} on FileSystemException {
//do something
}
/<code>

使用catch關鍵詞獲取異常對象,catch有兩個參數,第一個是異常對象,第二個是錯誤堆棧。

<code>try{
file.readAsStringSync();
} on FileSystemException catch (e){
print('exception: $e');
} catch(e, s){ //其餘類型
print('Exception details:\\n $e');
print('Stack trace:\\n $s');
}
/<code>

使用rethrow 拋給上一級處理:

<code> try{
file.readAsStringSync();
} on FileSystemException catch (e){
print('exception: $e');
} catch(e){
rethrow;
}
/<code>

finally

finally一般用於釋放資源等一些操作,它表示最後一定會執行的意思,即便try...catch中有return,它裡面的代碼也會承諾執行。

<code>try{
print('hello');

return;
} catch(e){
rethrow;
} finally{
print('finally');
}
/<code>

輸出:

<code>hello
finally
/<code>

Dart 是一門面向對象的編程語言,所有對象都是某個類的實例,所有類繼承了Object類。

一個簡單的類:

<code>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);
}
}
/<code>

類成員

Dart 通過. 來調用類成員變量和方法的。

<code>//創建對象,new 關鍵字可以省略
var p = Point(2, 2);

// Set the value of the instance variable y.
p.y = 3;

// Get the value of y.
assert(p.y == 3);

// Invoke distanceTo() on p.
num distance = p.distanceTo(Point(4, 4));
/<code>

你還可以通過.?來避免null對象。在Java 裡面,經常需要大量的空判斷來避免NullPonterException,這是讓人詬病Java的其中一個地方。而在Dart中,可以很方便地避免這個問題:

<code>// If p is non-null, set its y value to 4.
p?.y = 4;
/<code>

在 Dart 中,沒有private、protected、public這些關鍵詞,如果要聲明一個變量是私有的,則在變量名前添加下劃線_,聲明瞭私有的變量,只在本類庫中可見。

<code>class Point{
num _x;
num _y;
}
/<code>

構造器(Constructor)

如果沒有聲明構造器,Dart 會給類生成一個默認的無參構造器,聲明一個帶參數的構造器,你可以像 Java這樣:

<code>class Person{
String name;
int sex;

Person(String name, int sex){
this.name = name;
this.sex = sex;
}
}
/<code>

也可以使用簡化版:

<code>Person(this.name, this.sex);
/<code>

或者命名式構造器:

<code>Person.badGirl(){
this.name = 'Bad Girl';
this.sex = 1;
}
/<code>

你還可以通過factory關鍵詞來創建實例:

<code>Person.goodGirl(){
\tthis.name = 'good Girl';
\tthis.sex = 1;
}

factory Person(int type){
\treturn type == 1 ? Person.badGirl(): Person.goodGirl();
}
/<code>

factory對應到設計模式中工廠模式的語言級實現,在 Flutter 的類庫中有大量的應用,比如Map:

<code>// 部分代碼
abstract class Map {
\tfactory Map.from(Map other) = LinkedHashMap.from;
}
/<code>

如果一個對象的創建過程比較複雜,比如需要選擇不同的子類實現或則需要緩存實例等,你就可以考慮通過這種方法。在上面Map例子中,通過聲明 factory來選擇了創建子類LinkedHashMap(LinkedHashMap.from也是一個factory,裡面是具體的創建過程)。

如果你想在對象創建之前的時候還想做點什麼,比如參數校驗,你可以通過下面的方法:

<code> Person(this.name, this.sex): assert(sex == 1)
/<code>

在構造器後面添加的一些簡單操作叫做initializer list

在Dart中,初始化的順序如下:

  1. 執行initializer list;
  2. 執行父類的構造器;
  3. 執行子類的構造器。
<code>class Person{
String name;
int sex;

Person(this.sex): name = 'a', assert(sex == 1){
this.name = 'b';
print('Person');
}

}

class Man extends Person{
Man(): super(1){
this.name = 'c';
print('Man');
}
}

void main(){
Person person = Man();
print('name : ${person.name}');
}
/<code>

上面的代碼輸出為:

<code>Person
Man
name : c
/<code>

如果子類構造器沒有顯式調用父類構造器,那麼默認會調用父類的默認無參構造器。顯式調用父類的構造器:

<code>Man(height): this.height = height, super(1);
/<code>

重定向構造器:

<code>Man(this.height, this.age): assert(height > 0), assert(age > 0);

Man.old(): this(12, 60); //調用上面的構造器
/<code>

Getter 和 Setter

在 Dart 中,對 Getter 和 Setter 方法有專門的優化。即便沒有聲明,每個類變量也會默認有一個get方法,在隱含接口章節會有體現。

<code>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);
}
/<code>

抽象類

Dart 的抽象類和Java差不多,除了不可以實例化,可以聲明抽象方法之外,和一般類沒有區別。

<code>abstract class AbstractContainer {
num _width;

void updateChildren(); // 抽象方法,強制繼承子類實現該方法。

get width => this._width;

int sqrt(){
return _width * _width;
}
}
/<code>

隱含接口

Dart 中的每個類都隱含了定義了一個接口,這個接口包含了這個類的所有成員變量和方法,你可以通過implements關鍵詞來重新實現相關的接口方法:

<code>class Person {
//隱含了 get 方法
final _name;

Person(this._name);

String greet(String who) => 'Hello, $who. I am $_name.';
}

class Impostor implements Person {
// 需要重新實現
get _name => '';

// 需要重新實現
String greet(String who) => 'Hi $who. Do you know who I am?';
}
/<code>

實現多個接口:

<code>class Point implements Comparable, Location {...}
/<code>

繼承

和Java基本一致,繼承使用extends關鍵詞:

<code>class Television {
void turnOn() {
doSomthing();
}
}

class SmartTelevision extends Television {

@override
void turnOn() {
super.turnOn(); //調用父類方法
doMore();
}
}
/<code>

重載操作符

比較特別的是,Dart 還允許重載操作符,比如List類支持的下標訪問元素,就定義了相關的接口:

<code> E operator [](int index);
/<code>

我們通過下面的實例來進一步說明重載操作符:

<code>class MyList{

var list = [1,2,3];

operator [](int index){
return list[index];
}
}

void main() {
var list = MyList();
print(list[1]); //輸出 2
}
/<code>

擴展方法

這個特性也是Dart讓人眼前一亮的地方(Dart2.7之後才支持),可以對標到 JavaScript 中的 prototype。通過這個特性,你甚至可以給類庫添加新的方法:

<code>//通過關鍵詞 extension 給 String 類添加新方法
extension NumberParsing on String {
int parseInt() {
return int.parse(this);
}
}
/<code>

後面String對象就可以調用該方法了:

<code>print('42'.parseInt()); 
/<code>

枚舉類型

枚舉類型和保持和Java的關鍵詞一致:

<code>enum Color { red, green, blue }
/<code>

在switch中使用:

<code>// color 是 enmu Color 類型
switch(color){
case Color.red:
break;
case Color.blue:
break;
case Color.green:
break;
default:
break;
}
/<code>

枚舉類型還有一個index的getter,它是個連續的數字序列,從0開始:

<code>assert(Color.red.index == 0);
assert(Color.green.index == 1);
assert(Color.blue.index == 2);
/<code>

新特性:Mixins

這個特性進一步增強了代碼複用的能力,如果你有寫過Android的佈局XML代碼或者Freemaker模板的話,那這個特性就可以理解為其中inlclude 的功能。

<code>mixin Musical {
bool canPlayPiano = false;
bool canCompose = false;
bool canConduct = false;


void entertainMe() {
if (canPlayPiano) {
print('Playing piano');
} else if (canConduct) {
print('Waving hands');
} else {
print('Humming to self');
}
}
}
/<code>

通過with關鍵詞進行復用:

<code>class Musician extends Performer with Musical {
// ···
}

class Maestro extends Person
with Musical, Aggressive, Demented {
Maestro(String maestroName) {
name = maestroName;
canConduct = true;
}
}
/<code>

mixin類甚至可以通過on關鍵詞實現繼承的功能:

<code>mixin MusicalPerformer on Musician {
// ···
}
/<code>

類變量和類方法

<code>class Queue {
//類變量
static int maxLength = 1000;
// 類常量
static const initialCapacity = 16;
// 類方法
static void modifyMax(int max){
_maxLength = max;
}

}

void main() {
print(Queue.initialCapacity);
Queue.modifyMax(2);
print(Queue._maxLength);
}
/<code>

泛型

在面向對象的語言中,泛型主要的作用有兩點:

1、類型安全檢查,把錯誤扼殺在編譯期:

<code>var names = List<string>();
names.addAll(['Seth', 'Kathy', 'Lars']);
//編譯錯誤
names.add(42);
/<string>/<code>

2、增強代碼複用,比如下面的代碼:

<code>abstract class ObjectCache {
Object getByKey(String key);
void setByKey(String key, Object value);
}

abstract class StringCache {
String getByKey(String key);
void setByKey(String key, String value);
}
/<code>

你可以通過泛型把它們合併成一個類:

<code>abstract class Cache {
T getByKey(String key);
void setByKey(String key, T value);
}
/<code>

在Java中,泛型是通過類型擦除來實現的,但在Dart中實打實的泛型:

<code> var names = <string>[];
names.addAll(['Tom',"Cat"]);
// is 可以用於類型判斷
print(names is List<string>); // true
print(names is List); // true
print(names is List); //false
/<string>/<string>/<code>

你可以通過extends關鍵詞來限制泛型類型,這點和Java一樣:

<code>abstract class Animal{}
class Cat extends Animal{}
class Ext{
T data;
}

void main() {
var e = Ext(); // ok
var e1 = Ext<animal>(); // ok
var e2 = Ext(); // ok
var e3 = Ext(); // compile error
}
/<animal>
/<code>

使用類庫

有生命力的編程語言,它背後都有一個強大的類庫,它們可以讓我們站在巨人的肩膀上,又免於重新造輪子。

導入類庫

在Dart裡面,通過import關鍵詞來導入類庫。

內置的類庫使用dart:開頭引入:

<code>import 'dart:io';
/<code>

第三方類庫或者本地的dart文件用package:開頭:

比如導入用於網絡請求的dio庫:

<code>import 'package:dio/dio.dart';
/<code>

Dart 應用本身就是一個庫,比如我的應用名是ccsys,導入其他文件夾的類:

<code>import 'package:ccsys/common/net_utils.dart';
import 'package:ccsys/model/user.dart';
/<code>

如果你使用IDE來開發,一般這個事情不用你來操心,它會自動幫你導入的。

Dart 通過pub.dev來管理類庫,類似Java世界的Maven 或者Node.js的npm一樣,你可以在裡面找到非常多實用的庫。

解決類名衝突

如果導入的類庫有類名衝突,可以通過as使用別名來避免這個問題:

<code>import 'package:lib1/lib1.dart'; 

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

// 使用來自 lib1 的 Element
Element element1 = Element();

// 使用來自 lib2 的 Element
lib2.Element element2 = lib2.Element();

/<code>

導入部分類

在一個dart文件中,可能會存在很多個類,如果你只想引用其中幾個,你可以增加show或者hide來處理:

<code>//文件:my_lib.dart
class One {}

class Two{}

class Three{}
/<code>

使用show導入One和Two類:

<code>//文件:test.dart
import 'my_lib.dart' show One, Two;

void main() {
var one = One();
var two = Two();
//compile error
var three = Three();
}
/<code>

也可以使用hide排除Three,和上面是等價的:

<code>//文件:test.dart
import 'my_lib.dart' hide Three;

void main() {
var one = One();

var two = Two();
}
/<code>

延遲加載庫

目前只有在web app(dart2js)中才支持延遲加載,Flutter、Dart VM是不支持的,我們這裡僅做一下簡單介紹。

你需要通過deferred as來聲明延遲加載該類庫:

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

當你需要使用的時候,通過loadLibrary()加載:

<code>Future greet() async {
await hello.loadLibrary();
hello.printGreeting();
}
/<code>

你可以多次調用loadLibrary,它不會被重複加載。

異步支持

這個話題稍微複雜,我們將用另外一篇文章獨立討論這個問題,請留意下一篇內容。

  1. 學習Dart的十大理由
  2. A tour of the Dart language
  3. Dart 常量構造器的理解
  4. JavaScript 閉包

關於AgileStudio

我們是一支由資深獨立開發者和設計師組成的團隊,成員均有紮實的技術實力和多年的產品設計開發經驗,提供可信賴的軟件定製服務。


分享到:


相關文章: