天天看點

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 那樣聲明一個變量:

var name = 'Bob';           

編譯器會推導出

name

的類型是

String

類型,等價于:

String name = 'Bob';           

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

var name = 'Bob';
  
// 調用 String 的方法
print(name.toLowerCase());

// 編譯錯誤
// name = 1;           

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

dynamic

來聲明變量,這就跟 JavaScript 一樣了:

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

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

final

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

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

// 編譯錯誤
// name = 'Mary';           

其中

String

可以省略,Dart 編譯器足夠聰明地知道變量

name

的類型。

如果要聲明常量,可以使用

const

關鍵詞:

const PI = '3.14';

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

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

static const

内置類型

不像Java把類型分的特别細,比如整數類型,就有

byte

short

int

long

。Dart 的類型設計相當簡潔,這也是 Dart 容易上手的原因之一,可以了解為通過犧牲空間來換取效率吧。

數值類型

Dart 内置支援兩種數值類型,分别是

int

double

,它們的大小都是64位。

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


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

需要注意的是,在Dart中,所有變量值都是一個對象,

int

double

類型也不例外,它們都是

num

類型的子類,這點和

Java

JavaScript

都不太一樣:

// 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');           

字元串

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

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

Dart 采用了 JavaScript 中類似模闆字元串的概念,可以在字元串通過

${expression}

文法插入變量:

var s = "hello";
  
print('${s}, world!');
  
//可以簡化成:
print('$s, world!');

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

Dart 可以直接通過

==

來比較字元串:

var s1 = "hello";
var s2 = "HELLO";
assert(s1.toUpperCase() == s2);           

布爾類型

Dart 布爾類型對應為

bool

關鍵詞,它有

true

false

兩個值,這點和其他語言差別不大。值得一提的是,在Dart的條件語句

if

assert

表達式裡面,它們的值必須是

bool

類型,這點和 JavaScript 不同。

var s = '';
assert(s.isEmpty);

if(s.isNotEmpty){
// do something
}
  
//編譯錯誤,在JavaScript常用來判斷undefined
if(s){
}           

Lists

你可以把Dart中的

List

對應到 JavaScript 的數組或者 Java 中的

ArrayList

,但 Dart 的設計更為精巧。

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

var list = [];
list.add('Hello');
list.add(1);           

這裡

List

容器接受的類型是

dynamic

,你可以往裡面添加任何類型的對象,但如果像這樣聲明:

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

那麼Dart就會推導出這個

List

是個

List<int>

,從此這個

List

就隻能接受

int

類型資料了,你也可以顯式聲明

List

的類型:

var sList = List<String>();

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

上面右邊那個

const

的意思表示常量數組,在這裡你可以了解為一個給

children

指派了一個編譯期常量空數組,這樣的做法可以很好的節省記憶體,下面的例子可以讓大家更好的了解常量數組的概念:

var constList = const <int>[1,2];
constList[0] = 2; //編譯通過, 運作錯誤
constList.add(3); //編譯通過, 運作錯誤           

Dart2.3 增加了擴充運算符 (spread operator)

...

...?

,通過下面的例子你很容易就明白它們的用法:

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

如果擴充對象可能是

null

,可以使用

...?

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

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

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

甚至使用

for

來動态添加多個元素:

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

這種動态的能力使得 Flutter 在建構 Widget 樹的時候非常友善。

Sets

Set

的語意和其他語言的是一樣的,都是表示在容器中對象唯一。在Dart中,

Set

預設是

LinkedHashSet

實作,表示元素按添加先後順序排序。

聲明

Set

對象:

var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};           

周遊

Set

,周遊除了上面提到的

for...in

,你還可以使用類似 Java 的 lambada 中的

forEach

形式:

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

輸出結果:

fluorine
chlorine
bromine
iodine
astatine           

除了容器的對象唯一特性之外,其他基本和

List

是差不多的。

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

var promoActive = true;
// 動态添加元素
final navSet = {'Home', 'Furniture', promoActive? 'About':'Outlet'};           

Maps

Map

對象的聲明方式保持了 JavaScript 的習慣,Dart 中

Map

的預設實作是

LinkedHashMap

,表示元素按添加先後順序排序。

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

assert(gifts['first'] == 'partridge');           

添加一個鍵值對:

gifts['fourth'] = 'calling birds';           

Map

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

函數

在 Dart 中,函數本身也是個對象,它對應的類型是

Function

,這意味着函數可以當做變量的值或者作為一個方法入傳參數值。

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');
}           

輸出:

hello, Girl
hello, Boy           

對于隻有一個表達式的簡單函數,你還可以通過

=>

讓函數變得更加簡潔,

=> expr

在這裡相當于

{ return expr; }

,我們來看一下下面的語句:

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

相當于:

String hello(var name ){
  return 'hello, $name';
}           

參數

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

void enableFlags({bool bold, bool hidden}) {...}           

調用這個函數:

enableFlags(bold: false);
enableFlags(hidden: false);
enableFlags(bold: true, hidden: false);           

命名參數預設是可選的,如果你需要表達該參數必傳,可以使用

@required

void enableFlags({bool bold, @required bool hidden}) {}           

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

void enableFlags(bool bold, bool hidden) {}           

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

enableFlags(false, true);           

你可以使用

[]

來增加非必填參數:

void enableFlags(bool bold, bool hidden, [bool option]) {}           

另外,Dart 的函數還支援設定參數預設值:

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

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

匿名函數

顧名思意,匿名函數的意思就是指沒有定義函數名的函數。你應該對此不陌生了,我們在周遊

List

Map

的時候已經使用過了,通過匿名函數可以進一步精簡代碼:

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

閉包

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

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

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)
}           

結果如我們所料的那樣列印了

Flutter

在(1)執行完之後,

name

作為一個函數的局部變量,引用的對象不是應該被回收掉了嗎?但是當我們在内函數調用外部的

name

時,它依然可以神奇地被調用,這是為什麼呢?

這是因為Dart在運作内部函數時會形成閉包,閉包是由函數以及建立該函數的詞法環境組合而成,這個環境包含了這個閉包建立時所能通路的所有局部變量 。

我們簡單變一下代碼:

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

結果輸出是

Dart

,可以看到内部函數通路外部函數的變量時,是在同一個詞法環境中的。

傳回值

在Dart中,所有的函數都必須有傳回值,如果沒有的話,那将自動傳回

null

foo() {}

assert(foo() == null);           

流程控制

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

if-else

if(hasHause && hasCar){
    marry();
}else if(isHandsome){
    date();
}else{
    pass();
}           

循環

各種

for

:

var list = [1,2,3];

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

for(var i in list){}           

while

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

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);           

如果對象是

Iterable

類型,你還可以像Java的 lambada 表達式一樣:

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

switch

switch

可以用于

int

double

String

enum

等類型,

switch

隻能在同類型對象中進行比較,進行比較的類不要覆寫

==

運算符。

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

assert

在Dart中,

assert

語句經常用來檢查參數,它的完整表示是:

assert(condition, optionalMessage)

,如果

condition

false

,那麼将會抛出

[AssertionError]

異常,停止執行程式。

assert(text != null);

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

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

類其中一個方法注釋:

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

抛出異常

throw FormatException('Expected at least 1 section');           

throw

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

throw 'Out of llamas!';           

捕獲異常

可以通過

on

關鍵詞來指定異常類型:

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

使用

catch

關鍵詞擷取異常對象,

catch

有兩個參數,第一個是異常對象,第二個是錯誤堆棧。

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

rethrow

抛給上一級處理:

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

finally

finally

一般用于釋放資源等一些操作,它表示最後一定會執行的意思,即便

try...catch

中有

return

,它裡面的代碼也會承諾執行。

try{
     print('hello');
     return;
  } catch(e){
     rethrow;
  } finally{
     print('finally');
}           
hello
finally           

Dart 是一門面向對象的程式設計語言,所有對象都是某個類的執行個體,所有類繼承了

Object

類。

一個簡單的類:

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

類成員

Dart 通過

.

來調用類成員變量和方法的。

//建立對象,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));           

你還可以通過

.?

來避免

null

對象。在Java 裡面,經常需要大量的空判斷來避免

NullPonterException

,這是讓人诟病Java的其中一個地方。而在Dart中,可以很友善地避免這個問題:

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

在 Dart 中,沒有

private

protected

public

這些關鍵詞,如果要聲明一個變量是私有的,則在變量名前添加下劃線

_

,聲明了私有的變量,隻在本類庫中可見。

class Point{
  num _x;
  num _y;
}           

構造器(Constructor)

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

class Person{
  String name;
  int sex;
  
  Person(String name, int sex){
    this.name = name;
    this.sex = sex;
  }
}           

也可以使用簡化版:

Person(this.name, this.sex);           

或者命名式構造器:

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

factory

關鍵詞來建立執行個體:

Person.goodGirl(){
    this.name = 'good Girl';
    this.sex = 1;
}

factory Person(int type){
    return type == 1 ? Person.badGirl(): Person.goodGirl();
}           

factory

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

Map

// 部分代碼
abstract class Map<K, V> {  
    factory Map.from(Map other) = LinkedHashMap<K, V>.from;
}           

如果一個對象的建立過程比較複雜,比如需要選擇不同的子類實作或則需要緩存執行個體等,你就可以考慮通過這種方法。在上面

Map

例子中,通過聲明

factory

來選擇了建立子類

LinkedHashMap

LinkedHashMap.from

也是一個

factory

,裡面是具體的建立過程)。

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

Person(this.name, this.sex): assert(sex == 1)           

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

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

  1. 執行initializer list;
  2. 執行父類的構造器;
  3. 執行子類的構造器。
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}');
}           

上面的代碼輸出為:

Person
Man
name : c           

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

Man(height): this.height = height, super(1);           

重定向構造器:

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

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

Getter 和 Setter

在 Dart 中,對

Getter

Setter

方法有專門的優化。即便沒有聲明,每個類變量也會預設有一個

get

方法,在

隐含接口

章節會有展現。

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差不多,除了不可以執行個體化,可以聲明抽象方法之外,和一般類沒有差別。

abstract class AbstractContainer {
  num _width;

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

  get width => this._width;
  
  int sqrt(){
    return _width * _width;
  }
}           

隐含接口

Dart 中的每個類都隐含了定義了一個接口,這個接口包含了這個類的所有成員變量和方法,你可以通過

implements

關鍵詞來重新實作相關的接口方法:

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?';
}           

實作多個接口:

class Point implements Comparable, Location {...}           

繼承

和Java基本一緻,繼承使用

extends

class Television {
  void turnOn() {
    doSomthing();
  }
}

class SmartTelevision extends Television {

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

重載操作符

比較特别的是,Dart 還允許重載操作符,比如

List

類支援的下标通路元素,就定義了相關的接口:

E operator [](int index);           

我們通過下面的執行個體來進一步說明重載操作符:

class MyList{

  var list = [1,2,3];

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

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

擴充方法

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

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

後面

String

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

print('42'.parseInt());            

枚舉類型

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

enum Color { red, green, blue }           

switch

中使用:

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

枚舉類型還有一個

index

的getter,它是個連續的數字序列,從0開始:

assert(Color.red.index == 0);
assert(Color.green.index == 1);
assert(Color.blue.index == 2);           

新特性:Mixins

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

inlclude

的功能。

聲明一個

mixin

類:

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');
    }
  }
}           

通過

with

關鍵詞進行複用:

class Musician extends Performer with Musical {
  // ···
}

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

mixin

類甚至可以通過

on

關鍵詞實作繼承的功能:

mixin MusicalPerformer on Musician {
  // ···
}           

類變量和類方法

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

泛型

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

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

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

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

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

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

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

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

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

你可以通過

extends

關鍵詞來限制泛型類型,這點和Java一樣:

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

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

使用類庫

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

導入類庫

在Dart裡面,通過

import

關鍵詞來導入類庫。

内置的類庫使用

dart:

開頭引入:

import 'dart:io';           

了解更多内置的類庫可以檢視

第三方類庫或者本地的dart檔案用

package:

開頭:

比如導入用于網絡請求的

dio

庫:

import 'package:dio/dio.dart';           

Dart 應用本身就是一個庫,比如我的應用名是

ccsys

,導入其他檔案夾的類:

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

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

pub.dev

來管理類庫,類似Java世界的

Maven

或者Node.js的npm一樣,你可以在裡面找到非常多實用的庫。

解決類名沖突

如果導入的類庫有類名沖突,可以通過

as

使用别名來避免這個問題:

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

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

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

導入部分類

在一個dart檔案中,可能會存在很多個類,如果你隻想引用其中幾個,你可以增加

show

或者

hide

來處理:

//檔案:my_lib.dart
class One {}

class Two{}

class Three{}           

show

導入

One

Two

//檔案:test.dart
import 'my_lib.dart' show One, Two;

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

也可以使用

hide

排除

Three

,和上面是等價的:

//檔案:test.dart
import 'my_lib.dart' hide Three;

void main() {
  var one = One();
  var two = Two();
}           

延遲加載庫

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

你需要通過

deferred as

來聲明延遲加載該類庫:

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

當你需要使用的時候,通過

loadLibrary()

加載:

Future greet() async {
  await hello.loadLibrary();
  hello.printGreeting();
}           

你可以多次調用

loadLibrary

,它不會被重複加載。

異步支援

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

參考資料

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

關于 AgileStudio

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