Dart 基礎入門

劉斯龍發表於2019-02-11

參考連結

Built-in Data Types

  • Numbers
    • int
    • double
  • Strings
  • Booleans
  • Lists
  • Maps
  • Runes
  • Symbols

Dart 中所有的資料型別都是 Object, 所以資料型別的預設值為 null

在 Dart 中定義一個變數的方法: 例如:定義一個整型的變數 age,並賦上初始值為 10

int age = 10;
var age = 10;
複製程式碼

上面這兩種寫法都是可以的,第二種方式會自動推斷出 age 的型別為整型。

定義一個字串型別的變數 name,並賦上初始值為 "jack"

String name = "jack";
var name = "jack";
複製程式碼

布林值型別

bool isAlive = true;
var isAlive = true;
複製程式碼

三目運算

var name_1;  
var name_2 = "Jack";
複製程式碼

和 Java 中的三目運算一樣:

// name_1是否為空?為空 則取 name_2 否則 取值 name_1 的值
var finalName = name_1 == null ? name_2 : name_1;
複製程式碼

在 Dart 中判空還有一種寫法,使用 ?? 來判斷,如下:

var finalName = name_1 ?? name_2;
複製程式碼

表示 如果 name_1 不為空,則返回 name_1,否則返回 name_2

Switch

switch 的條件可以是 int, String, bool

for loop

List letters = ["a", "b", "c", "d"]; 
for (String letter in letters) { 
  print(letter);  
}
複製程式碼

For 迴圈執行邏輯如下: Initialize --> Condition check --> Code Execute --> Increment / Decrement

While 迴圈執行邏輯: Condition Check --> Code Execute --> Increment / Decrement

Do - While 迴圈執行邏輯: Code Execute --> Increment / Decrement --> Condition Check

Function

Functions in Dart are Object

Dart 中的函式可以作為變數,也可以作為引數傳遞給其他的函式。

使用簡短語法定義函式中的表示式

void main() {  
  sum(2, 3);  
  
  int result = reduce(2, 3);  
  print(result);  
}  
  
void sum(int a, int b) => print("a + b = ${a + b}");  
  
int reduce(int a, int b) => a - b;
複製程式碼

Output:

a + b = 5
-1
複製程式碼

引數

Dart 中的引數分為兩種,必須的可選的可選引數又包含三種分別為 位置引數(Positional)具名引數(Named)預設引數(Default)

引數的分類如下:

Dart 基礎入門

Required Parameters

void main() {  
  printName("Jack", "Rose");  
}  
  
// Required Parameters  
void printName(String name1, String name2) {  
  print('Name 1 is $name1, Name 2 is $name2');  
}
複製程式碼

可選引數

可選引數使用 [ ] 來包裹

void main() {  
  printCountries("China", "USA", "India");  
}  
  
// Optional Positional Parameters  
void printCountries(String name1, String name2, String name3) {  
  print('Name 1 is $name1');  
  print('Name 2 is $name2');  
  print('Name 3 is $name3');  
}
複製程式碼

Output

Name 1 is China
Name 2 is USA
Name 3 is India
複製程式碼

這個時候我們想讓第三個位置的引數變為可選的,就是使用者呼叫的時候可以不傳遞。怎麼寫呢,如下:

void main() {  
  printCountries("China", "USA");  
}  
  
// Optional Positional Parameters  
void printCountries(String name1, String name2, [String name3]) {  
  print('Name 1 is $name1');  
  print('Name 2 is $name2');  
  print('Name 3 is $name3');  
}
複製程式碼

Output:

Name 1 is China
Name 2 is USA
Name 3 is null
複製程式碼

那如果我們想讓最後兩個引數都變成可選的呢,也就是我們在呼叫上述方法的時候只需要傳遞一個引數:

void main() {  
  printCountries("China");  
}  
  
// Optional Positional Parameters  
void printCountries(String name1, [String name2, String name3]) {  
  print('Name 1 is $name1');  
  print('Name 2 is $name2');  
  print('Name 3 is $name3');  
}
複製程式碼

Output:

Name 1 is China
Name 2 is null
Name 3 is null
複製程式碼

具名引數

具名引數使用 { } 來包裹 具名引數傳遞的時候,和引數順序無關

當一個方法中有大量的引數的時候,我們可以使用具名引數來傳遞,可以防止引數傳遞錯誤:

void main() {  
  printCities("Beijing", name2: "Shanghai", name3: "Guangzhou");  
}  
  
  
// Named Parameters  
void printCities(String name1, {String name2, String name3}) {  
  print('Name 1 is $name1');  
  print('Name 2 is $name2');  
  print('Name 3 is $name3');  
}
複製程式碼

Output:

Name 1 is Beijing
Name 2 is Shanghai
Name 3 is Guangzhou
複製程式碼

我們也可以值傳遞具名引數中的某一部分:

printCities("Beijing", name2: "Shanghai");
複製程式碼

Output:

Name 1 is Beijing
Name 2 is Shanghai
Name 3 is null
複製程式碼

或者

printCities("Beijing");
複製程式碼

Output:

Name 1 is Beijing
Name 2 is null
Name 3 is null
複製程式碼

預設值引數

顧名思義,預設引數就是可以為引數設定一個預設值。

void main() {  
  printNameByDefaultParameters("jack");  
}

// default parameters  
void printNameByDefaultParameters(String name1, {String name2 = "rose"}) {  
  print('name 1 is $name1');  
  print('name 2 is $name2');  
}
複製程式碼

上述 printNameByDefaultParameters 方法中,我們為 name2 引數設定了預設值為 rose ,然後我們在呼叫該方法的時候就可以只傳遞引數 name1

Output:

name 1 is jack
name 2 is rose
複製程式碼

當然,我們也可以不適用預設的引數的值,我們可以為其傳遞值,從而覆蓋預設值:

printNameByDefaultParameters("jack", name2: "Han Meimei");
複製程式碼

Output

name 1 is jack
name 2 is Han Meimei
複製程式碼

Exception

異常處理

當程式終端或者程式崩潰的時候,這個時候就需要我們處理異常。 我們來看一個例子:

void main() {  
  int result = 12 ~/ 0;  
  print('The result is $result');  
}
複製程式碼

上述程式會丟擲除數不能為0的異常:

Unhandled exception:
IntegerDivisionByZeroException
#0      int.~/ (dart:core/runtime/libintegers.dart:18:7)
...
複製程式碼

如果我們知道程式會丟擲哪一個異常,在 Dart 中,我們可以使用 try on 來捕獲,如下:

void main() {  
  try{  
    int result = 12 ~/ 0;  
  print('The result is $result');  
  } on IntegerDivisionByZeroException {  
    print('Cannot devide by zero');  
  }  
}
複製程式碼

輸出:

Cannot devide by zero
複製程式碼

如果我們不知道程式會丟擲什麼異常,使用 try catch 來捕獲:

try{  
  int result = 12 ~/ 0;  
  print('The result is $result');  
} catch (e) {  
  print('The exception is $e');  
}
複製程式碼

Output:

The exception is IntegerDivisionByZeroException
複製程式碼

那如果我們想知道在丟擲異常之前發生了什麼,可以使用 Stack Trace 如下:

try{  
  int result = 12 ~/ 0;  
  print('The result is $result');  
} catch (e, s) {  
  print('The exception is $e');  
  print('Stack Trace \n $s');  
}
複製程式碼

Output:

The exception is IntegerDivisionByZeroException
Stack Trace 
 #0      int.~/ (dart:core/runtime/libintegers.dart:18:7)
#1      main (file:///Users/liusilong/Dev/Flutter/workspace/DartDemo/src/youtube/exception/Demo1.dart:24:21)
#2      _startIsolate.<anonymous closure> (dart:isolate/runtime/libisolate_patch.dart:289:19)
#3      _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:171:12)
複製程式碼

Finally

無論異常是否丟擲,finally 語句塊總會被執行:

try{  
  int result = 12 ~/ 0;  
  print('The result is $result');  
} catch (e) {  
  print('The exception is $e');  
}finally{  
  print('finally is always executed');  
}
複製程式碼

Output:

The exception is IntegerDivisionByZeroException
finally is always executed
複製程式碼

上述程式碼即使不丟擲異常,finally 語句塊中還是會被執行:

try{  
  int result = 12 ~/ 3;  
  print('The result is $result');  
} catch (e) {  
  print('The exception is $e');  
}finally{  
  print('finally is always executed');  
}
複製程式碼

Output:

The result is 4
finally is always executed
複製程式碼

自定義異常

下面來定義一個方法,該方法不能接受小於 0 的引數,然後我們自定義一個異常,來捕獲傳入該方法的引數是否小於 0 。

// 自定義異常
class MyException implements Exception {  
  String errorMsg() {  
    return "count 不能小於0";  
  }  
}

// counter 引數不能小於 0
void enterCounter(int counter) {  
  if(counter < 0) {  
    throw MyException();  
  }  
}

// 呼叫
try {  
  enterCounter(-2);  
} catch(e) {  
  print(e.errorMsg());  
}
複製程式碼

Output:

count 不能小於0
複製程式碼

物件導向

定義一個類

下面我們來定義一個 Student 類,並宣告一些例項變數和例項方法:

class Student {  
  // 預設值為 null  
  int id = 7;  
  // 預設值為 null  
  String name;  
  
  void study(){  
    print('${this.name} is now studying');  
  }  
  
  void sleep(){  
    print('${this.name} is now sleeping');  
  }  
}
複製程式碼

接下來我們例項化一個 Student 然後為其屬性賦值,並呼叫其方法:

void main() {  
  var stu = Student();  
  stu.id = 8;  
  stu.name = "jack";  
  print('${stu.id} and ${stu.name}');  
  stu.study();  
  stu.sleep();  
}
複製程式碼

Output:

8 and jack
jack is now studying
jack is now sleeping
複製程式碼

定義建構函式

預設建構函式

同 Java 一樣,在 Dart 中,一個類也會有一個預設的無參的建構函式,我們也可以將其定義出來:

// default constructor  
Student() {  
  print('This is default constructor');  
}
複製程式碼

帶有引數的建構函式

// 帶有引數的構造方法  
Student(int id, String name) {  
  this.id = id;  
  this.name = name;  
}
複製程式碼

預設構造方法和帶有引數的構造方法不能同時存在,否則在編譯期就會報錯,如下:

Dart 基礎入門

引數化建構函式

一般情況下,我們的在構造方法中傳遞的引數會給當前類的例項變數賦值如:this.id = id,這個時候,我們可以直接使用引數化建構函式,如下:

// 引數化建構函式
Student(this.id, this.name);
複製程式碼

那麼,如果我們在一個類中先要定義多個構造方法怎麼辦,這個時候可以使用 命名建構函式

我們在構造方法中同樣可以使用具名引數,如下:

void main(){  
  var p = Person("Jack");  
  p.getInfo();  
  
  var p2 = Person("Rose", age: 18);  
  p2.getInfo();  
}

class Person{  
  String name;  
  int age;  
  
  Person(this.name, {this.age});  
  
  void getInfo(){  
    print('name is $name, age is $age');  
  }  
}
複製程式碼

Output:

name is Jack, age is null
name is Rose, age is 18
複製程式碼

命名建構函式

// 引數化建構函式  
Student(this.id, this.name);  
  
Student.myConstructor(int id) {  
  this.id = id;  
  this.name = "Jack";  
}  
  
Student.anotherConstructor(String name) {  
  this.id = 10;
  this.name = name;
}
複製程式碼

呼叫如下:

void main() {
  var stu1 = Student(8, "jack");  
  print('${stu1.id} and ${stu1.name}');  
  
  var stu2 = Student.myConstructor(7);  
  print('${stu2.id} and ${stu2.name}');  
  
  var stu3 = Student.anotherConstructor("Rose");  
  print('${stu3.id} and ${stu3.name}');  
}
複製程式碼

Output:

8 and jack
7 and Jack
10 and Rose
複製程式碼

Getter 和 Setter

預設的 Getter 和 Setter

下面我們還是定義一個 Student 類,裡面有 nameage 兩個屬性:

class Student {  
  String name;  
  int age;  
}
複製程式碼

接著我們來例項化出一個 Student 的例項,並且為其屬性賦值:

void main() {  
  var stu = Student();  
  // 呼叫預設的 Setter 方法設定 name  
  stu.name = "Jack";  
  // 呼叫預設的 Getter 方法獲取 name  
  print('name is ${stu.name}');  
}
複製程式碼

注意:上述程式碼中,我們分別呼叫了預設的 Setter 方法和 Getter 方法。

Output:

name is Jack
複製程式碼

自定義的 Getter 和 Setter

下面我們在 Student 類中新新增一個屬性為 address,我們來自定義這個屬性的 getter 和 setter 方法:

class Student {  
  String name; // 預設的 getter 和 setter  
  int age; // 預設的 getter 和 setter  
  
  // new line  
  String address; // 自定義的 getter 和 setter  
 
  set setAddress(String address) {  
    this.address = address;  
  print('呼叫了 address 的 setter 方法,值為 $address');  
  }  
  
  get getAddress {  
    print('呼叫了 address 的 getter 方法');  
  return address;  
  }  
}
複製程式碼

呼叫

stu.setAddress = "China";  
print('stu address is ${stu.getAddress}');
複製程式碼

Output:

呼叫了 address 的 setter 方法,值為 China
呼叫了 address 的 getter 方法
stu address is China
複製程式碼

繼承

和 Java 中的繼承類似,Dart 中也是使用 extends 關鍵字來實現繼承,同樣,也可以實現方法過載

下面例子中,我們定義一個父類 Animal,子類 Dog 來演示他們之間的繼承關係以及方法過載的實現:


void main() {  
  var dog = Dog();  
  dog.bark();  
  dog.eat();  
}

// 父類  
class Animal {  
  void eat() {  
    print('Animal is eating');  
  }  
}

// 子類  
class Dog extends Animal {  
  // 方法過載  
  void eat() {  
    // 通過 super 呼叫父類方法  
  super.eat();  
  print('Dog is eating');  
  }  
  
  void bark() {  
    print('Brak!');  
  }  
}
複製程式碼

Output:

Brak!
Animal is eating
Dog is eating
複製程式碼

繼承中的構造方法

一般情況下,如果父類沒有有參構造方法,那麼我們可以直接這樣:

// 父類
class Animal {  
  String color;  
  // 預設的構造方法  
  Animal() {}  
}  

// 子類
class Dog extends Animal {  
  final String name;  
  // 帶有一個引數的構造方法  
  Dog(this.name):super() {}  
}
複製程式碼

上述情況中,Animal 類中的構造方法沒有引數,我們在 Dog 類中可以將 Dog(this.name):super() 後面部分省略,寫為Dog(this.name)

那麼如果父類中有引數的情況下,上述 Dog 類中的寫法就會報錯了,如下:

Dart 基礎入門

提示大概意思就是在父類中找不到一個包含0個引數的構造方法,我們之前也說過,預設的構造方法和帶有引數的構造方法不能同時存在。

所以我們可以這樣寫:

class Animal {  
  String color;  
  
  Animal(String color) {}  
}  
  
class Dog extends Animal {  
  final String name;  
  
  Dog(this.name, String color) : super(color) {}  
}
複製程式碼

同樣的,我們也可以在子類中使用命名構造方法


class Dog extends Animal {  
  final String name;  
  
  Dog(this.name, String color) : super(color) {}  
  
  Dog.myDog(this.name): super("White"){  
  
  }  
}

複製程式碼

我們在例項化 Dog 類的時候可以這樣:

void main() {  
  var dog1 = Dog("petter", "Blcak");  
  
  var dog2 = Dog.myDog("Hello");  
}
複製程式碼

當然,如果父類中有命名構造方法,我們在子類中構造方法中去呼叫父類的命名構造方法:


class Animal {  
  String color;  
  
  Animal(String color) {}  
  
  Animal.myAnimal() {}  
}  
  
class Dog extends Animal {  
  final String name;  
  
  Dog(this.name, String color) : super(color) {}  
  
  Dog.myDog(this.name) : super("White") {}  
  
  // 呼叫父類的 命名構造 方法
  Dog.smallDog(this.name) : super.myAnimal() {}  
}

複製程式碼

注意:

預設情況下,子類中的構造方法呼叫的是父類的無參構造方法 父類的構造方法總是在子類的構造方法之前呼叫 如果在父類中沒有預設構造方法(無參構造方法),那麼需要我們手動呼叫父類中的某一個構造方法

抽象類和抽象方法

和 Java 中的抽象類一樣,Dart 中的抽象類也是使用 abstract 關鍵字類修飾。如: abstract class shape{} 抽象類不能直接被例項化,需要實現一個子類來繼承它。 抽象類中可以包含抽象方法和非抽象方法。抽象方法不能包含方法體且子類必須實現,非抽象方法可以包含方法體且子類可以重寫。 抽象類中也可以定義的例項變數。 抽象方法只能定義在抽象類中。

void main() {
  var rect = Rectangle();
  rect.draw();
  rect.drawShape();
}

// 抽象類
abstract class Shape {
  // 定義例項變數
  int x;
  int y;
  
  // 非抽象方法,子類可以重寫
  void drawShape() {
    print('draw shape');
  }

  // 抽象方法,子類必須實現
  void draw();
}

// 子類
class Rectangle extends Shape {

  // 實現父類的抽象方法(必選)
  @override
  void draw() {
    // TODO: implement draw
    print('draw Rectangle...');
  }

  // 重寫父類的方法(可選)
  @override
  void drawShape() {
    // TODO: implement drawShape
    super.drawShape();
    print('rectangle drawshape');
  }
}
複製程式碼

Output:

draw Rectangle...
draw shape
rectangle drawshape
複製程式碼

介面

Java 中的介面是使用 Interface 關鍵字類修飾一個類,但是 Dart 中沒有 Interface 關鍵字。Dart 中的介面依然是使用 class 類定義:

void main() {
  var b = B();
  b.hello();
  b.hi();
}

class A {
  void hello() {
    print('A hello');
  }
}

class C {
  void hi() {
    print('C hi');
  }
}

// class B 實現了 介面 A,C
class B implements A, C {
  @override
  void hello() {
    // TODO: implement hello
    print('B hello');
  }

  @override
  void hi() {
    // TODO: implement hi
    print('B hi');
  }
}

複製程式碼

Output:

B hello
B hi
複製程式碼

那麼我們在實現的介面方法中能不能使用 super.hello() 呢?

Dart 基礎入門

如上圖,是不可以的,大概意思就是 hello 方法在父型別中是抽象的。

如果我們將 implements 改為 extends 之後就可以了。

Dart 基礎入門

總結: Dart 沒有特定的語法來宣告一個介面。 Dart 中的介面就是一個普通的類。 當你需要在子類中重新實現父類中的所有方法的時候,可以使用介面。 在介面的實現類中,會被強制要求實現父類中的所有方法。 可以implements多個類,但是隻能 extends 一個類。

靜態方法和變數

和 Java 中的靜態變數類似,Dart 中的靜態變數也是通過 static 關鍵字來宣告,通過 類名.變數名 來訪問:

void main() {
  print(Circle.pi);
}

class Circle {
  // 定義一個靜態變數
  static double pi = 3.14;
}
複製程式碼

Output:

3.14
複製程式碼

但是,在 Dart 中如果我們使用類的例項去訪問該類中的靜態變數,編譯時就會報錯:

Dart 基礎入門

大概意思就是靜態變數不能通過例項來訪問。

同樣的,靜態方法也是通過 static 關鍵字類宣告,通過 類名.方法名 來呼叫; 同樣的,靜態方法也不能通過例項來訪問。

void main() {
  // 訪問靜態變數
  print(Circle.pi);
  // 呼叫靜態方法
  Circle.calculateAre();
}

class Circle {
  // 定義靜態變數
  static double pi = 3.14;

  // 定義靜態方法
  static void calculateAre() {
    print('some code');
  }
}
複製程式碼

Output:

3.14
some code
複製程式碼

靜態方法中只能訪問靜態變數和靜態方法,不能訪問例項變數或者例項方法。

Dart 基礎入門

總結:

靜態變數也被稱為 類變數 靜態方法也被稱為 類方法 靜態變數都是懶載入的,意思就是當你在程式中使用到的時候該靜態變數才會被初始化。 靜態變數或者靜態方法與類的例項無關,它們都屬於類本身,所以我麼在靜態方法中不能使用 this 關鍵字 在靜態方法中,我們只能訪問靜態方法或者靜態變數,不能訪問該類中的例項變數或者例項方法

高階函式和 Lambda 表示式

在 Dart 中,方法也是一個物件 Lambda 是一個沒有方法名的方法

下面我們來看看示例:

void main() {
  // 普通方法呼叫
  addTwoNumbers(2, 3);

  // 沒有返回值的 Lambda
  Function addTwoNumbersByFunction = (int a, int b) {
    var sum = a + b;
    print('$sum');
  };

  addTwoNumbersByFunction(2, 3);

  // 帶有返回值的 Lambda
  var sum = (int a, int b) {
    return a + b;
  };

  print('${sum(3, 4)}');
}

void addTwoNumbers(int a, int b) {
  var sum = a + b;
  print('$sum');
}
複製程式碼

Output:

5
5
7
複製程式碼

我們還可以使用另外一種簡短的語法來表示 Lambda,使用 => 來表示

void main(){
    // 使用簡短語法宣告沒有返回值的 Lambda
    Function addTwoNumbersWithArrows = (int a, int b) => print('${a + b}');

    // 呼叫
    addTwoNumbersWithArrows(3, 8);
      
      
    // 使用簡單語法宣告帶有返回值的 Lambda
    var sumWithArrows = (int a, int b) => a + b;
    // 呼叫
    print('${sumWithArrows(3, 7)}');
}
複製程式碼

Output:

11
10
複製程式碼

高階函式

高階函式的定義

  • 可以接受一個 Function 作為引數的函式
  • 可以返回一個 Function 的函式
  • 以上兩者都允許

下面我們來寫一個用於四則運算的高階函式:

void main() {
  var plusResult = arithmetic(12, 3, (a, b) => a + b);
  var reduceResult = arithmetic(12, 3, (a, b) => a - b);
  var multiplyResult = arithmetic(12, 3, (a, b) => a * b);
  var divideResult = arithmetic(12, 3, (a, b) => a / b);

  print('plus result is $plusResult');
  print('reduce result is $reduceResult');
  print('myltiply result is $multiplyResult');
  print('divide result is $divideResult');
}

// 接受一個 Function 引數,並且返回了一個 Function
double arithmetic(double a, double b, Function function) {
  return function(a, b);
}
複製程式碼

上述程式碼中 arithmetic 方法接受了一個 Function,並且將其作為返回值返回; 而引數 Function 是接受兩個 double 型別的引數。

我們在上一小節中講到 高階函式 可以使用沒有名稱的方法來表示也可以使用 Lambda 來表示。

上述程式碼中使用的是 Lambda 作為高階函式進行傳遞的,那麼如果我們使用沒有名稱的方法來表示,如下:

  var modelResult = arithmetic(12, 3, (a, b) {
    return a % b;
  });
  print('model result is $modelResult');
複製程式碼

Output:

plus result is 15.0
reduce result is 9.0
myltiply result is 36.0
divide result is 4.0
model result is 0.0
複製程式碼

Collections

固定長度的 List

我們來宣告一個長度為 5 的整型 List

定長的集合在被定義之後,長度就不能被改變

void main() {
  // 宣告一個固定長度的集合
  List<int> numList = List(5);
  // 新增元素
  numList.add(9);
  // 刪除元素
  numList.remove(9);
  // 刪除指定位置上的元素
  numList.removeAt(0);
  // 清空集合
  numList.clear();
}
複製程式碼

那麼如果我們只想上述程式碼之後會怎樣呢?答案是會報錯,因為上述程式碼中的4中操作在固定長度的集合中都不支援。

Output:

Unhandled exception:
Unsupported operation: Cannot add to a fixed-length list
...
複製程式碼

這裡順帶提一下,上述程式碼中我們知道了會丟擲 Unsupported 異常,那麼根據前面的知識,我們可以使用 try..on.. 來捕獲,就當複習一下:

void main() {
  try {
    // 宣告一個固定長度的集合
    List<int> numList = List(5);
    // 新增元素
    numList.add(9);
    // 刪除元素
    numList.remove(9);
    // 刪除指定位置上的元素
    numList.removeAt(0);
    // 清空集合
    numList.clear();
  } on UnsupportedError {
    print('操作不支援...');
  }
}
複製程式碼

Output:

操作不支援...
複製程式碼

那麼回到我們的問題,固定長度的集合要怎麼新增刪除元素呢?如下:

void main() {
  // 宣告一個固定長度的集合
  List<int> numList = List(5);

  numList[0] = 1;
  numList[1] = 2;
  numList[2] = 3;
  numList[3] = 4;
  numList[4] = 5;

  print('遍歷元素:');
  for (int value in numList) {
    print(value);
  }

  print('----更新第一個元素為 10------');

  numList[0] = 10;

  print('遍歷元素:');
  numList.forEach((value) => print(value));

  print('----將第一個元素置為NULL---');

  numList[0] = null;

  print('遍歷元素:');
  for (int i = 0; i < numList.length; i++) {
    print('${numList[i]}');
  }
}
複製程式碼

上述程式碼中,我們使用了索引的方式來給固定長度的 List 賦值,並使用三種方式來遍歷,第一種為 for..in ;第二種為 Lambda 表示式;第三種為常規的 for 迴圈

Output:

遍歷元素:
1
2
3
4
5
----更新第一個元素為 10------
遍歷元素:
10
2
3
4
5
----將第一個元素置為NULL---
遍歷元素:
null
2
3
4
5
複製程式碼

動態長度的 List

我們直接看示例:

void main() {
  // 動態長度的 集合
  List<int> list = List();

  list.add(1);
  list.add(2);

  list.forEach((value) => print('$value'));
  print('');
  
  list.removeAt(0);
  list.forEach((value) => print('$value'));
  print('');
  
  list.add(3);
  list[0] = null;
  list.forEach((value) => print('$value'));
}
複製程式碼

Output:

1
2

2

null
3

複製程式碼

還有一種宣告方法:

  // 這樣也是一個動態的集合
  List<String> letterList = ["A", "B", "C"];

  letterList.add("D");
  letterList.add("E");

  letterList.forEach((letter) => print('$letter'));
複製程式碼

Output:

A
B
C
D
E
複製程式碼

Set

Set 是無序的;Set 是不允許新增重複元素;Set 不能使用下標來獲取裡面裡面的元素,因為它是無序 的。

void main() {
  // 方式一:
  Set<String> letterSet = Set.from(["A", "B", "C"]);
  letterSet.add("D");
  letterSet.add("E");
  // 重複的元素將被忽視
  letterSet.add("A");

  // 使用 for..in.. 遍歷 Set
  for (String letter in letterSet) {
    print('$letter');
  }

  print('');
  
  // 方式二:
  Set<int> numSet = Set();
  numSet.add(0);
  numSet.add(1);
  numSet.add(2);

  // 使用 Lambda 遍歷 Set
  numSet.forEach((value) => print('$value'));
}
複製程式碼

Output:

A
B
C
D
E

0
1
2
複製程式碼

Map

Dart 中 Map 的特性和 Java 中的類似,都是以鍵值對的形式存放,Map 中的鍵是唯一的,但是值可以重複,Map 也是無序的。

下面看看 Dart 中 Map 的基本操作:

void main() {
  Map<String, String> letterMap = Map();
  letterMap["a"] = "A";
  letterMap["b"] = "B";
  letterMap["c"] = "C";

  // 檢查是否存在某個 key
  letterMap.containsKey("a");
  // 更新某個 key 對應的 value,注意 value 為一個 Lambda 表示式
  letterMap.update("a", (value) => "${value}AA");

  // 獲取 Map 的長度
  letterMap.length;
  // 刪除 Map 中的某個元素
  letterMap.remove("c");
  // 清空 Map
  letterMap.clear();

  // 遍歷所有的 key
  for (String key in letterMap.keys) {
    print('$key');
  }

  print('');

  // 遍歷所有的 value
  for (String value in letterMap.values) {
    print('$value');
  }

  print('');

  // 遍歷所有的 key-value
  letterMap.forEach((key, value) => print('$key == $value'));
}
複製程式碼

上述程式碼中使用的是構造方法的方式來建立 Map,我們還可以使用一下方式來建立:

  Map<String, int> studentScoreMap = {
    "Jack": 90,
    "Rost": 100,
    "Mary": 30,
    "LiLei": 56
  };

  studentScoreMap.forEach((name, source) => print('$name == $source'));
複製程式碼

Output:

Jack == 90
Rost == 100
Mary == 30
LiLei == 56
複製程式碼

Callable

Callable 能讓我們像呼叫方法一樣呼叫某個類,不過我們在類中需要實現 call 方法:

void main() {
  var personOne = Person();
  var info = personOne("Jack", 18);
  print('$info');
}

class Person {
  // 實現 call 方法
  String call(String name, int age) {
    return "$name is $age years old";
  }
}
複製程式碼

Output:

Jack is 18 years old
複製程式碼

如有錯誤,還請指出。謝謝!!!