Dart語法篇之基礎語法(一)

極客熊貓發表於2019-10-27

簡述:

又是一段新的開始,Dart這門語言相信很多人都是通過Flutter這個框架才瞭解的,因為Flutter相比Dart更被我們所熟知。很多人遲遲不願嘗試Flutter原因大多數是因為學習成本高,顯然擺在面前的是需要去重新學習一門新的語言dart,然後再去學習一個開發框架Flutter,再加上很多莫名奇妙的坑,不說多的就從Github上Flutter專案issue數來看坑是著實不少,所以很多人也就望而卻步了。當然這個問題是一個新的技術框架剛開始都可能存在的,但我們更需要看到Flutter框架跨平臺技術思想先進性。

為什麼要開始一系列Dart相關的文章?

很簡單,就是為了更好地開發Flutter, 其實開發Flutter使用Dart的核心知識點並不需要太過於全面,有些東西根本用不到,所以該系列文章也將是有選擇性選取Dart一些常用技術點講解。另一方面,讀過我文章的小夥伴就知道我是Kotlin的狂熱忠實粉, 其實語言都是相通,你可以從Dart身上又能看到Kotlin的身影,所以我上手Dart非常快,可以對比著學習。所以後期Dart文章我會將Dart與Kotlin作為對比講解,所以如果你學過Kotlin那麼恭喜你,上手Dart將會非常快。

該系列Dart文章會講哪些內容呢?

本系列文章主要會涉及以下內容: dart基本語法、變數常量和型別推導、集合、函式、物件導向的Mixins、泛型、生成器函式、Async和Await、Stream和Future、Isolate和EventLoop以及最後基本介紹下DartVM的工作原理。

一、Hello Dart

這是第一個Hello Dart程式,很多程式入口都是從main函式開始,所以dart也不例外,一起來看下百變的main函式

//main標準寫法
void main() {
  print('Hello World!');//注意: Dart和Java一樣表示式以分號結尾,寫習慣Kotlin的小夥伴需要注意了, 這可能是你從Kotlin轉Dart最大不適之一。
}

//dart中所有函式的型別都是可以省略的,省略的時候返回值型別為dynamic
main() {
  print('Hello World!');  
}

//如果函式內部只有一個表示式,可以省略大括號,使用"=>"箭頭函式; 
//而對於Kotlin則是如果只有一個表示式,可以省略大括號,使用"="連線,類似 fun main(args: Array<String>) = println('Hello World!')
void main() => print('Hello World!');

//最簡寫形式
main() => print('Hello World!');
複製程式碼

二、資料型別

在dart中的一切皆是物件,包括數字、布林值、函式等,它們和Java一樣都繼承於Object, 所以它們的預設值也就是null. 在dart主要有: 布林型別bool、數字型別num(數字型別又分為int,double,並且兩者父類都是num)、字串型別String、集合型別(List, Set, Map)、Runes類和Symbols型別(後兩個用的並不太多)

1、布林型別(bool)

在dart中和C語言一樣都是使用bool來宣告一個布林型別變數或常量,而在Kotlin則是使用Boolean 來宣告,但是一致的是它對應的值只有兩個true和false.

main() {
    bool isClosed = true;//注意,dart還是和Java類似的 [型別][變數名]方式宣告,這個和Kotlin的 [變數名]:[型別]不一樣.
    bool isOpened = false;
}
複製程式碼

2、數字型別(num、int、double)

在dart中num、int、double都是類,然後int、double都繼承num抽象類,這點和Kotlin很類似,在Kotlin中Number、Int、Double都是類,然後Int、Double都繼承於Number. 注意,但是在dart中沒有float, short, long型別

![](/Users/mikyou/Library/Application Support/marktext/images/2019-10-24-22-02-40-image.png)

main() {
    double pi = 3.141592653;
    int width = 200;
    int height = 300;
    print(width / height);//注意:這裡和Kotlin、Java都不一樣,兩個int型別相除是double型別小數,而不是整除後的整數。
    print(width ~/ height);//注意: 這才是dart整除正確姿勢
}
複製程式碼

此外和Java、Kotlin一樣,dart也擁有一些數字常用的函式:

main() {
    print(3.141592653.toStringAsFixed(3)); //3.142 保留有效數字
    print(6.6.floor());//6向下取整
    print((-6.6).ceil()); //-6 向上取整
    print(9.9.ceil()); //10 向上取整
    print(666.6.round()); //667 四捨五入
    print((-666.6).abs()); // 666.6 取絕對值
    print(666.6.toInt()); //666 轉化成int,這中toInt、toDouble和Kotlin類似
    print(999.isEven); //false 是否是偶數
    print(999.isOdd); //true 是否是奇數
    print(666.6.toString()); //666.6 轉化成字串
}
複製程式碼

3、字串型別(String)

在Dart中支援單引號、雙引號、三引號以及$字串模板用法和Kotlin是一模一樣的。

main() {
    String name = 'Hello Dart!';//單引號
    String title = "'Hello Dart!'";//雙引號
    String description = """
          Hello Dart! Hello Dart!
          Hello Dart!
          Hello Dart! Hello Dart!
    """;//三引號
    num value = 2;
    String result = "The result is $value";//單值引用
    num width = 200;
    num height = 300;
    String square = "The square is ${width * height}";//表示式的值引用
}
複製程式碼

和Kotlin一樣,dart中也有很多字串操作的方法,比如字串拆分、子串等

main() {
  String url = "https://mrale.ph/dartvm/";

  print(url.split("://")[0]); //字串分割split方法,類似Java和Kotlin

  print(url.substring(3, 9)); //字串擷取substring方法, 類似Java和Kotlin

  print(url.codeUnitAt(0)); //取當前索引位置字元的UTF-16碼

  print(url.startsWith("https")); //當前字串是否以指定字元開頭, 類似Java和Kotlin

  print(url.endsWith("/")); //當前字串是否以指定字元結尾, 類似Java和Kotlin

  print(url.toUpperCase()); //大寫, 類似Java和Kotlin

  print(url.toLowerCase()); //小寫, 類似Java和Kotlin

  print(url.indexOf("ph")); //獲取指定字元的索引位置, 類似Java和Kotlin

  print(url.contains("http")); //字串是否包含指定字元, 類似Java和Kotlin

  print(url.trim()); //去除字串的首尾空格, 類似Java和Kotlin

  print(url.length); //獲取字串長度

  print(url.replaceFirst("t", "A")); //替換第一次出現t字元位置的字元

  print(url.replaceAll("m", "M")); //全部替換, 類似Java和Kotlin
}
複製程式碼

4、型別檢查(is和is!)和強制型別轉換(as)

和Kotlin一樣,dart也是通過 is 關鍵字來對型別進行檢查以及使用 as 關鍵字對型別進行強制轉換,如果判斷不是某個型別dart中使用 is! , 而在Kotlin中正好相反則用 !is 表示。

main() {
    int number = 100;
    double distance = 200.5;
    num age = 18;
    print(number is num);//true
    print(distance is! int);//true
    print(age as int);//18
}
複製程式碼

5、Runes和Symbols型別

在Dart中的Runes和Symbols型別使用並不多,這裡做個簡單的介紹, Runes型別是UTF-32位元組單元定義的Unicode字串,Unicode可以使用數字表示字母、數字和符號,然而在dart中String是一系列的UTF-16的位元組單元,所以想要表示32位的Unicode的值,就需要用到Runes型別。我們一般使用\uxxxx這種形式來表示一個Unicode碼,xxxx 表示4個十六進位制值。當十六進位制資料多餘或者少於4位時,將十六進位制數放入到花括號中,例如,微笑表情(?)是\u{1f600}。而Symbols型別則用得很少,一般用於Dart中的反射,但是注意在Flutter中禁止使用反射。

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));
}
複製程式碼

6、Object型別

在Dart中所有東西都是物件,都繼承於Object, 所以可以使用Object可以定義任何的變數,而且賦值後,型別也可以更改。

main() {
    Object color = 'black';
    color = 0xff000000;//執行正常,0xff000000型別是int, int也繼承於Object   
}
複製程式碼

7、dynamic型別

在Dart中還有一個和Object型別非常類似的型別那就是dynamic型別,下面講到的var宣告的變數未賦值的時候就是dynamic型別, 它可以像Object一樣可以改變型別。dynamic型別一般用於無法確定具體型別, 注意: 建議不要濫用dynamic,一般儘量使用Object, 如果你對Flutter和Native原生通訊PlatformChannel程式碼熟悉的話,你會發現裡面大量使用了dynamic, 因為可能native資料型別無法對應dart中的資料型別,此時dart接收一般就會使用dynamic.

Object和dynamic區別在於: Object會在編譯階段檢查型別,而dynamic不會在編譯階段檢查型別。

main() {
    dynamic color = 'black';
    color = 0xff000000;//執行正常,0xff000000型別是int, int也繼承於Object
}
複製程式碼

三、變數和常量

1、var關鍵字

在dart中可以使用var來替代具體型別的宣告,會自動推導變數的型別,這是因為var並不是直接儲存值,而是儲存值的物件引用,所以var可以宣告任何變數。這一點和Kotlin不一樣,在Kotlin中宣告可變的變數都必須需要使用var關鍵字,而Kotlin的型別推導是預設行為和var並沒有直接關係。注意: 在Flutter開發一般會經常使用var宣告變數,以便於可以自動推導變數的型別。

main() {
  int colorValue = 0xff000000;
  var colorKey = 'black'; //var宣告變數 自動根據賦值的型別,推導為String型別 
  // 使用var宣告集合變數 
  var colorList = ['red', 'yellow', 'blue', 'green'];
  var colorSet = {'red', 'yellow', 'blue', 'green'};
  var colorMap = {'white': 0xffffffff, 'black': 0xff000000};
}
複製程式碼

但是在使用var宣告變數的時候,需要注意的是: 如果var宣告的變數開始不初始化,不僅值可以改變它的型別也是可以被修改的,但是一旦開始初始化賦值後,它的型別就確定了,後續不能被改變。

main() {
  var color; // 僅有宣告未賦值的時候,這裡的color的型別是dynamic,所以它的型別是可以變的 
  color = 'red';
  print(color is String); //true 
  color = 0xffff0000;
  print(color is int); //true 

  var colorValue = 0xffff0000; //宣告時並賦值,這裡colorValue型別已經推匯出為int,並且確定了型別 
  colorValue = 'red'; //錯誤,這裡會丟擲編譯異常,String型別的值不能賦值給int型別 
  print(colorValue is int); //true
}
複製程式碼

2、常量(final和const)

在dart中宣告常量可以使用constfinal 兩個關鍵字,注意: 這兩者的區別在於如果常量是編譯期就能初始化的就用const(有點類似Kotlin中的const val) 如果常量是執行時期初始化的就用final(有點類似Kotlin中的val)

main() {    
  const PI = 3.141592653;//const定義常量    
  final nowTime = DateTime.now();//final定義常量
}
複製程式碼

四、集合(List、Set、Map)

1、集合List

在dart中的List和Kotlin還是很大的區別,換句話說Dart整個集合型別系統的劃分都和Kotlin都不一樣,比如Dart中集合就沒有嚴格區分成可變集合(Kotlin中MutableList)和不變集合(Kotlin中的List),在使用方式上你會感覺它更像陣列,但是它是可以隨意對元素增刪改成的。

  • List初始化方式

    main() {
        List<String> colorList = ['red', 'yellow', 'blue', 'green'];//直接使用[]形式初始化
        var colorList = <String> ['red', 'yellow', 'blue', 'green'];
    }
    複製程式碼
  • List常用的函式

    main() {
        List<String> colorList = ['red', 'yellow', 'blue', 'green'];
        colorList.add('white');//和Kotlin類似通過add新增一個新的元素
        print(colorList[2]);//可以類似Kotlin一樣,直接使用陣列下標形式訪問元素
        print(colorList.length);//獲取集合的長度,這個Kotlin不一樣,Kotlin中使用的是size
        colorList.insert(1, 'black');//在集合指定index位置插入指定的元素
        colorList.removeAt(2);//移除集合指定的index=2的元素,第3個元素
        colorList.clear();//清除所有元素
        print(colorList.sublist(1,3));//擷取子集合
        print(colorList.getRange(1, 3));//獲取集合中某個範圍元素
        print(colorList.join('<--->'));//類似Kotlin中的joinToString方法,輸出: red<--->yellow<--->blue<--->green
        print(colorList.isEmpty);
        print(colorList.contains('green'));    
    }
    複製程式碼
  • List的遍歷方式

    main() {
        List<String> colorList = ['red', 'yellow', 'blue', 'green'];
        //for-i遍歷
        for(var i = 0; i < colorList.length; i++) {//可以使用var或int
            print(colorList[i]);        
        }
        //forEach遍歷
        colorList.forEach((color) => print(color));//forEach的引數為Function. =>使用了箭頭函式
        //for-in遍歷
        for(var color in colorList) {
            print(color);
        }
        //while+iterator迭代器遍歷,類似Java中的iteator
        while(colorList.iterator.moveNext()) {
            print(colorList.iterator.current);
        }
    }
    複製程式碼

2、集合Set

集合Set和列表List的區別在於 集合中的元素是不能重複 的。所以新增重複的元素時會返回false,表示新增不成功.

  • Set初始化方式

    main() {
        Set<String> colorSet= {'red', 'yellow', 'blue', 'green'};//直接使用{}形式初始化
        var colorList = <String> {'red', 'yellow', 'blue', 'green'};
    }
    複製程式碼
  • 集合中的交、並、補集,在Kotlin並沒有直接給到計算集合交、並、補的API

    main() {
        var colorSet1 = {'red', 'yellow', 'blue', 'green'};
        var colorSet2 = {'black', 'yellow', 'blue', 'green', 'white'};
        print(colorSet1.intersection(colorSet2));//交集-->輸出: {'yellow', 'blue', 'green'}
        print(colorSet1.union(colorSet2));//並集--->輸出: {'black', 'red', 'yellow', 'blue', 'green', 'white'}
        print(colorSet1.difference(colorSet2));//補集--->輸出: {'red'}
    }
    複製程式碼
  • Set的遍歷方式(和List一樣)

      main() {
        Set<String> colorSet = {'red', 'yellow', 'blue', 'green'};
        //for-i遍歷
        for (var i = 0; i < colorSet.length; i++) {
          //可以使用var或int
          print(colorSet[i]);
        }
        //forEach遍歷
        colorSet.forEach((color) => print(color)); //forEach的引數為Function. =>使用了箭頭函式
        //for-in遍歷
        for (var color in colorSet) {
          print(color);
        }
        //while+iterator迭代器遍歷,類似Java中的iteator
        while (colorSet.iterator.moveNext()) {
          print(colorSet.iterator.current);
        }
      }
    複製程式碼

3、集合Map

集合Map和Kotlin類似,key-value形式儲存,並且 Map物件的中key是不能重複的

  • Map初始化方式

    main() {
        Map<String, int> colorMap = {'white': 0xffffffff, 'black':0xff000000};//使用{key:value}形式初始化
     var colorMap = <String, int>{'white': 0xffffffff, 'black':0xff000000};
    }
    複製程式碼
  • Map中常用的函式

    main() {
        Map<String, int> colorMap = {'white': 0xffffffff, 'black':0xff000000};
        print(colorMap.containsKey('green'));//false
        print(colorMap.containsValue(0xff000000));//true
        print(colorMap.keys.toList());//['white','black']
        print(colorMap.values.toList());//[0xffffffff, 0xff000000]
        colorMap['white'] = 0xfffff000;//修改指定key的元素
        colorMap.remove('black');//移除指定key的元素
    }
    複製程式碼
  • Map的遍歷方式

    main() {
        Map<String, int> colorMap = {'white': 0xffffffff, 'black':0xff000000};
        //for-each key-value
        colorMap.forEach((key, value) => print('color is $key, color value is $value'));
    }
    複製程式碼
  • Map.fromIterables將List集合轉化成Map

    main() {
        List<String> colorKeys = ['white', 'black'];
        List<int> colorValues = [0xffffffff, 0xff000000];
        Map<String, int> colorMap = Map.fromIterables(colorKeys, colorValues);
    }
    複製程式碼

4、集合常用的操作符

dart對於集合操作的也非常符合現代語言的特點,含有豐富的集合操作符API,可以讓你處理結構化的資料更加簡單。

main() {
  List<String> colorList = ['red', 'yellow', 'blue', 'green'];
  //forEach箭頭函式遍歷
  colorList.forEach((color) => {print(color)});
  colorList.forEach((color) => print(color)); //箭頭函式遍歷,如果箭頭函式內部只有一個表示式可以省略大括號

  //map函式的使用
  print(colorList.map((color) => '$color_font').join(","));

  //every函式的使用,判斷裡面的元素是否都滿足條件,返回值為true/false
  print(colorList.every((color) => color == 'red'));

  //sort函式的使用
  List<int> numbers = [0, 3, 1, 2, 7, 12, 2, 4];
  numbers.sort((num1, num2) => num1 - num2); //升序排序
  numbers.sort((num1, num2) => num2 - num1); //降序排序
  print(numbers);

  //where函式使用,相當於Kotlin中的filter操作符,返回符合條件元素的集合
  print(numbers.where((num) => num > 6));

  //firstWhere函式的使用,相當於Kotlin中的find操作符,返回符合條件的第一個元素,如果沒找到返回null
  print(numbers.firstWhere((num) => num == 5, orElse: () => -1)); //注意: 如果沒有找到,執行orElse程式碼塊,可返回一個指定的預設值

  //singleWhere函式的使用,返回符合條件的第一個元素,如果沒找到返回null,但是前提是集合中只有一個符合條件的元素, 否則就會丟擲異常
  print(numbers.singleWhere((num) => num == 4, orElse: () => -1)); //注意: 如果沒有找到,執行orElse程式碼塊,可返回一個指定的預設值

  //take(n)、skip(n)函式的使用,take(n)表示取當前集合前n個元素, skip(n)表示跳過前n個元素,然後取剩餘所有的元素
  print(numbers.take(5).skip(2));

  //List.from函式的使用,從給定集合中建立一個新的集合,相當於clone一個集合
  print(List.from(numbers));

  //expand函式的使用, 將集合一個元素擴充套件成多個元素或者將多個元素組成二維陣列展開成平鋪一個一位陣列
  var pair = [
    [1, 2],
    [3, 4]
  ];
  print('flatten list: ${pair.expand((pair) => pair)}');

  var inputs = [1, 2, 3];
  print('duplicated list: ${inputs.expand((number) =>[
    number,
    number,
    number
  ])}');
}
複製程式碼

五、流程控制

1、for迴圈

main() {
    List<String> colorList = ['red', 'yellow', 'blue', 'green'];
    for (var i = 0; i < colorList.length; i++) {//可以用var或int
        print(colorList[i]);
    }
}
複製程式碼

2、while迴圈

main() {
    List<String> colorList = ['red', 'yellow', 'blue', 'green'];
    var index = 0;
    while (index < colorList.length) {
        print(colorList[index++]);
    }
}
複製程式碼

3、do-while迴圈

main() {
    List<String> colorList = ['red', 'yellow', 'blue', 'green'];
    var index = 0;
    do {
        print(colorList[index++]);
    } while (index < colorList.length);
}
複製程式碼

4、break和continue

main() {
    List<String> colorList = ['red', 'yellow', 'blue', 'green'];
    for (var i = 0; i < colorList.length; i++) {//可以用var或int
        if(colorList[i] == 'yellow') {
            continue;
        }
        if(colorList[i] == 'blue') {
            break;
        }
        print(colorList[i]);
    }
}
複製程式碼

5、if-else

void main() {
  var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
  for (var i = 0; i < numbers.length; i++) {
    if (numbers[i].isEven) {
      print('偶數: ${numbers[i]}');
    } else if (numbers[i].isOdd) {
      print('奇數: ${numbers[i]}');
    } else {
      print('非法數字');
    }
  }
}
複製程式碼

6、三目運算子(? : )

void main() {
  var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
  for (var i = 0; i < numbers.length; i++) {
      num targetNumber = numbers[i].isEven ? numbers[i] * 2 : numbers[i] + 4;
      print(targetNumber);
  }
}
複製程式碼

7、switch-case語句

Color getColor(String colorName) {
  Color currentColor = Colors.blue;
  switch (colorName) {
    case "read":
      currentColor = Colors.red;
      break;
    case "blue":
      currentColor = Colors.blue;
      break;
    case "yellow":
      currentColor = Colors.yellow;
      break;
  }
  return currentColor;
}
複製程式碼

8、Assert(斷言)

在dart中如果條件表示式結果不滿足條件,則可以使用 assert 語句中斷程式碼的執行。特別是在Flutter原始碼中隨處可見都是assert斷言的使用。注意: 斷言只在檢查模式下執行有效,如果在生產模式執行,則斷言不會執行。

assert(text != null);//text為null,就會中斷後續程式碼執行
assert(urlString.startsWith('https'));
複製程式碼

六、運算子

1、算術運算子

名稱 運算子 例子
+ var result = 1 + 1;
- var result = 5 - 1;
* var result = 3 * 5;
/ var result = 3 / 5; //0.6
整除 ~/ var result = 3 ~/ 5; //0
取餘 % var result = 5 % 3; //2

2、條件運算子

名稱 運算子 例子
大於 > 2 > 1
小於 < 1 < 2
等於 == 1 == 1
不等於 != 3 != 4
大於等於 >= 5 >= 4
小於等於 <= 4 <= 5

3、邏輯運算子

名稱 運算子 例子
|| 2 > 1 || 3 < 1
&& 2 > 1 && 3 < 1
!(2 > 1)

4、位運算子

名稱 運算子
位與 &
位或 |
位非 ~
異或 ^
左移 <<
右移 >>

5、三目運算子

condition ? expr1 : expr2

var isOpened = (value == 1) ? true : false;
複製程式碼

6、空安全運算子

操作符 解釋
result = expr1 ?? expr2 若expr1為null, 返回expr2的值,否則返回expr1的值
expr1 ??= expr2 若expr1為null, 則把expr2的值賦值給expr1
result = expr1?.value 若expr1為null, 就返回null,否則就返回expr1.value的值
  • 1、result = expr1 ?? expr2

    如果發現expr1為null,就返回expr2的值,否則就返回expr1的值, 這個類似於Kotlin中的 result = expr1 ?: expr2

    main() {
        var choice = question.choice ?? 'A';
        //等價於
        var choice2;
        if(question.choice == null) {
            choice2 = 'A';
        } else {
            choice2 = question.choice;
        }
    }
    複製程式碼
  • 2、expr1 ??= expr2 等價於 expr1 = expr1 ?? expr2 (轉化成第一種)

    main() {
        var choice ??= 'A';
        //等價於
        if(choice == null) {
            choice = 'A';
        }
    }
    複製程式碼
  • 3、result = expr1?.value

    如果expr1不為null就返回expr1.value,否則就會返回null, 類似Kotlin中的 ?. 如果expr1不為null,就執行後者

    var choice = question?.choice;
    //等價於
    if(question == null){
        return null;
    } else {
        return question.choice;
    }
    
    question?.commit();
    //等價於
    if(question == null){
        return;//不執行commit()
    } else {
        question.commit();//執行commit方法    
    }
    複製程式碼

7、級聯操作符(..)

級聯操作符是 .., 可以讓你對一個物件中欄位進行鏈式呼叫操作,類似Kotlin中的apply或run標準庫函式的使用。

question
    ..id = '10001'
    ..stem = '第一題: xxxxxx'
    ..choices = <String> ['A','B','C','D']
    ..hint = '聽音訊做題';
複製程式碼

Kotlin中的run函式實現對比

question.run {
    id = '10001'
    stem = '第一題: xxxxxx'
    choices = listOf('A','B','C','D')
    hint = '聽音訊做題'    
}
複製程式碼

8、運算子過載

在dart支援運算子自定義過載,使用operator關鍵字定義過載函式

class Vip {
  final int level;
  final int score;

  const Vip(this.level, this.score);

  bool operator >(Vip other) =>
      level > other.level || (level == other.level && score > other.score);

  bool operator <(Vip other) =>
      level < other.level || (level == other.level && score < other.score);

  bool operator ==(Vip other) =>
      level == other.level &&
      score == other.level; //注意: 這段程式碼可能在高版本的Dart中會報錯,在低版本是OK的
  //上述程式碼,在高版本Dart中,Object中已經過載了==,所以需要加上covariant關鍵字重寫這個過載函式。
  @override
  bool operator ==(covariant Vip other) =>
      (level == other.level && score == other.score);

  @override
  int get hashCode => super.hashCode; //伴隨著你還需要重寫hashCode,至於什麼原因大家應該都知道
}


main() {
    var userVip1 = Vip(4, 3500);
    var userVip2 = Vip(4, 1200);
    if(userVip1 > userVip2) {
        print('userVip1 is super vip');
    } else if(userVip1 < userVip2) {
        print('userVip2 is super vip');
    }
}
複製程式碼

七、異常

dart中的異常捕獲方法和Java,Kotlin類似,使用的也是try-catch-finally; 對特定異常的捕獲使用on關鍵字. dart中的常見異常有: NoSuchMethodError(當在一個物件上呼叫一個該物件沒有 實現的函式會丟擲該錯誤)、ArgumentError (呼叫函式的引數不合法會丟擲這個錯誤)

main() {
  int num = 18;
  int result = 0;
  try {
    result = num ~/ 0;
  } catch (e) {//捕獲到IntegerDivisionByZeroException
    print(e.toString());
  } finally {
    print('$result');
  }
}

//使用on關鍵字捕獲特定的異常
main() {
  int num = 18;
  int result = 0;
  try {
    result = num ~/ 0;
  } on IntegerDivisionByZeroException catch (e) {//捕獲特定異常
    print(e.toString());
  } finally {
    print('$result');
  }
}
複製程式碼

八、函式

在dart中函式的地位一點都不亞於物件,支援閉包和高階函式,而且dart中的函式也會比Java要靈活的多,而且Kotlin中的一些函式特性,它也支援甚至比Kotlin支援的更全面。比如支援預設值引數、可選引數、命名引數等.

1、函式的基本用法

main() {
    print('sum is ${sum(2, 5)}');
}

num sum(num a, num b) {
    return a + b;
}
複製程式碼

2、函式引數列表傳參規則

//num a, num b, num c, num d 最普通的傳參: 呼叫時,引數個數和引數順序必須固定
add1(num a, num b, num c, num d) {
  print(a + b + c + d);
}

//[num a, num b, num c, num d]傳參: 呼叫時,引數個數不固定,但是引數順序需要一一對應, 不支援命名引數
add2([num a, num b, num c, num d]) {
  print(a + b + c + d);
}

//{num a, num b, num c, num d}傳參: 呼叫時,引數個數不固定,引數順序也可以不固定,支援命名引數,也叫可選引數,是dart中的一大特性,這就是為啥Flutter程式碼那麼多可選屬性,大量使用可選引數
add3({num a, num b, num c, num d}) {
  print(a + b + c + d);
}

//num a, num b, {num c, num d}傳參: 呼叫時,a,b引數個數固定順序固定,c,d引數個數和順序也可以不固定
add4(num a, num b, {num c, num d}) {
  print(a + b + c + d);
}

main() {
  add1(100, 100, 100, 100); //最普通的傳參: 呼叫時,引數個數和引數順序必須固定
  add2(100, 100); //呼叫時,引數個數不固定,但是引數順序需要一一對應, 不支援命名引數(也就意味著順序不變)
  add3(
      b: 200,
      a: 200,
      c: 100,
      d: 100); //呼叫時,引數個數不固定,引數順序也可以不固定,支援命名引數(也就意味著順序可變)
  add4(100, 100, d: 100, c: 100); //呼叫時,a,b引數個數固定順序篤定,c,d引數個數和順序也可以不固定
}
複製程式碼

3、函式預設引數和可選引數(以及與Kotlin對比)

dart中函式的預設值引數和可選引數和Kotlin中預設值引數和命名引數一致,只是寫法上不同而已

add3({num a, num b, num c, num d = 100}) {//d就是預設值引數,給的預設值是100
   print(a + b + c + d);
}

main() {
    add3(b: 200, a: 100, c: 800);
}
複製程式碼

與Kotlin對比

fun add3(a: Int, b: Int, c: Int, d: Int = 100) {
    println(a + b + c + d)
}

fun main(args: Array<String>) {
    add3(b = 200, a = 100, c = 800)
}
複製程式碼

4、函式型別與高階函式

在dart函式也是一種型別Function,可以作為函式引數傳遞,也可以作為返回值。類似Kotlin中的FunctionN系列函式

main() {
  Function square = (a) {
    return a * a;
  };

  Function square2 = (a) {
    return a * a * a;
  };

  add(3, 4, square, square2)
}

num add(num a, num b, [Function op, Function op2]) {
  //函式作為引數傳遞
  return op(a) + op2(b);
}
複製程式碼

5、函式的簡化以及箭頭函式

在dart中的如果在函式體內只有一個表示式,那麼就可以使用箭頭函式來簡化程式碼,這點也和Kotlin類似,只不過在Kotlin中人家叫lambda表示式,只是寫法上不一樣而已。

add4(num a, num b, {num c, num d}) {
  print(a + b + c + d);
}

add5(num a, num b, {num c, num d})  =>  print(a + b + c + d);
複製程式碼

九、物件導向

在dart中一切皆是物件,所以物件導向在Dart中依然舉足輕重,下面就先通過一個簡單的例子認識下dart的物件導向,後續會繼續深入。

1、類的基本定義和使用

abstract class Person {
    String name;
    int age;
    double height;
    Person(this.name, this.age, this.height);//注意,這裡寫法可能大家沒見過, 這點和Java是不一樣,這裡實際上是一個dart的語法糖。但是這裡不如Kotlin,Kotlin是直接把this.name傳值的過程都省了。
    //與上述的等價程式碼,當然這也是Java中必須要寫的程式碼
    Person(String name, int age, double height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }   
    //然而Kotlin很徹底只需要宣告屬性就行,下面是Kotlin實現程式碼
    abstract class Person(val name: String, val age: Int, val height: Double)     
}

class Student extends Person {//和Java一樣同時使用extends關鍵字表示繼承
    Student(String name, int age, double height, double grade): super(name, age, height);//在 Dart裡:類名(變數,變數,...) 是建構函式的寫法, :super()表示該構造呼叫父類,這裡構造時傳入三個引數
}
複製程式碼

2、類中屬性的getter和setter訪問器(類似Kotlin)

abstract class Person {
  String _name; ////相當於kotlin中的var 修飾的變數有setter、getter訪問器,在dart中沒有訪問許可權, 預設_下劃線開頭變數表示私有許可權,外部檔案無法訪問
  final int _age;//相當於kotlin中的val 修飾的變數只有getter訪問器
  Person(this._name, this._age); //這是上述簡寫形式

  //使用set關鍵字 計算屬性 自定義setter訪問器
  set name(String name) => _name = name;
  //使用get關鍵字 計算屬性 自定義getter訪問器
  bool get isStudent => _age > 18;
}
複製程式碼

總結

這是dart的第一篇文章,主要就是從整體上介紹了下dart的語法,當然裡面還有一些東西需要深入,後續會繼續深入探討。整體看下有沒有覺得Kotlin和dart語法很像,其實裡面有很多特性都是現代程式語言的特性,包括你在其他語言中同樣能看到比如swift等。就到這裡,後面繼續聊dart和flutter...

我的公眾號

這裡有最新的Dart、Flutter、Kotlin相關文章以及優秀國外文章翻譯,歡迎關注~~~

Dart語法篇之基礎語法(一)

相關文章