Dart語法

本文只是簡單總結下 dart 的經常使用語法,加深下印象。想學習 dart 語法推薦去官網編程

1、重要概念

  • 任何一個變量都是對象,每一個對象都是類的實例,甚至是數字、函數、null都是對象。全部的對象都繼承自 Object 類。
  • 雖然 dart 是強類型語言,不過類型聲明是可選的,由於 dart 能夠推斷類型。如 var num = 1;num 會被推斷爲 int,當你知道沒有明確的類型時,可使用 dynamic 來修飾變量。
  • Dart 支持泛型,如 List<int>List<dynamic>(一系列任意類型的對象)。
  • Dart 支持頂級函數,也能夠在類或對象中定義函數,也能夠函數嵌套函數。
  • 一樣也支持頂級變量,在類或對象中定義變量。
  • Dart 沒有訪問修飾符,若是想要私有的,則變量名稱前綴加上 _
  • Dart 具備表達式,如條件表達式。

2、變量

varbash

var 修飾的變量能夠表示任意類型,當變量被賦值後,它自動會推斷出變量是什麼類型。網絡

var name = "sun"; // 自動推斷爲 String 類型
複製代碼

dynamic閉包

dynamic 修飾的變量也能夠表示任意類型,它和 var 的區別是不須要推斷出類型。這樣即便在以後的賦值類型不一樣也不會報錯。併發

dynamic name = "sun";
name = 1; 	// 賦值int類型值也不會報錯
複製代碼

final異步

final 修飾的變量只能被賦值一次,並且賦值必須發生在構造函數執行以前:async

class Test{
  final x = 1;
  Test(){}
}
// 或 
class Test{
  final x;
  Test(this.x){}
}
複製代碼

constide

乍一看 const 和 final 很相似,都只能被賦值一次,但 const 其實更復雜些。函數式編程

  • const 修飾的變量在編譯期就已經肯定,運行期不會再改變了,所以想上面 final 的第二種用法(構造函數傳參賦值)這種運行期才執行的是不行的。函數

  • const 通常在聲明在頂級域,若是想在類中聲明,那麼必須加上 static 修飾。

    const X = 1;
    const Y = X+1;
    class Test{
      static const Z = 3;
    }
    複製代碼
  • const 修飾的值只會建立一次,每次使用到時都會複用。如:

    var list = const [1,2,3];
    複製代碼

    list 列表會被賦值爲 [1,2,3],以後也能夠再賦值,這些都是同樣的。不過因爲 const 修飾了 [1,2,3],這個常量值以後不會再去開闢一個新的內存空間,而是每次賦值都會複用。這個特性在開發中特別有用,對於一些須要頻繁建立對象、集合但每次建立都是固定不變的地方,就會節省不少內存及性能開銷。下面是複用一個對象的例子:

    class Test{
      final x;
      const Test(this.x);
    }
    var test = const Test("hello");
    複製代碼

    這個時候 Test("hello") 這個對象永遠只會建立一次。

3、內置類型

1. Numbers

int: 在 Dart 中 int 佔8個字節,取值範圍是 -2^63 ~ 2^63-1,若是編譯成 JavaScript 則範圍變成 -2^53 ~ 2^53-1。

double: 也佔8個字節,double 和 int 都是 num 的子類型。如下是一些互轉操做。

var one = int.parse('1'); // String to int
var onePointOne = double.parse('1.1'); // String to double
String oneAsStr = 1.toString(); // int to String
String onePointOneAsStr = 1.1.toString(); // double to String
複製代碼

2. String

Dart 字符串是用 UTF-16 編碼的(對編碼有疑問的能夠看 字符編碼的概念 )。

3. bool

和其餘語言同樣,true or false。

4. List

var list = [1,2,3]; // 推斷爲 List<int> 類型。
List<String> list1 = List<String>(10);	// 泛型參數類型爲 String 而且長度爲10的列表
複製代碼

Dart 中提供擴展操做符來爲集合(List, Set, Map等)插入多個節點,如:

var list = [1,2,3];
var list1 = [0, ...list];
複製代碼

5. Set

和 List 不一樣之處在於裏面的item是惟一的,用花括號包括item。

var set = {1,2,1};	// 事實上set只有 {1,2}
var set1 = Set<String>();
複製代碼

6. Map

和其餘語言同樣是key-value映射集合。

var map = {
  "key1": "value1",
  "key2": "value2",
}
var map1 = Map();
map1['key1'] = "value1";
複製代碼

7. Rune

Rune 也是字符串,可是它是 UTF-32 編碼的,不過UTF-16也是能夠經過擴展表示4個字節的。暫時不清楚這個樂行的真實意圖。

8. Symbol

Symbol 屬於編譯時常量,經過名字來引用標識符頗有價值。它主要用於,代碼被混淆後不少變量名、方法名之類的都被混淆了,此時沒法經過反射得到。通常 Android 中會採起添加混淆文件的作法,目前還不知道 dart 是否有這種東西。不過使用 Symbol 能夠找到對應的變量、方法名,具體能夠看下 Symbol

4、函數

Dart 是面向對象的語言,所以萬物皆對象,函數也是一種 Function 類型,能夠直接把函數賦值給變量。一個函數也能夠只包含有一個表達式:

var fun = getFun(1);
bool getFun(num) => num != 1;
複製代碼

1. 可選參數

// 命名參數
void test({bool x = false, String y}); // 調用時顯示指定參數名並給其賦值,如 test(y: "xxx");

// 位置參數
void test([bool x, String y = 'test']); // 調用時須要按順序傳參,如 test(true),但test("hello")就會報錯
複製代碼

2. 函數做爲一級對象使用

意思就是函數既能夠做爲參數傳遞也能夠賦值給變量。函數式編程的關鍵。

3. 匿名函數

不須要聲明一個函數,直接在須要函數傳遞的地方寫一個去掉函數名的函數便可,格式如 (context) {} 這麼個東西。 4. 閉包

嵌套的函數能訪問外部函數的變量。

Function makeAdder(num addBy) {
  return (num i) => addBy + i;
}

void main() {
  var add2 = makeAdder(2);	
  var add4 = makeAdder(4);
  print(add2(3)); // 5
  print(add4(3)); // 7
}
複製代碼

基本上能夠認爲把外部函數的變量看作本身的變量,可是有一個問題,就是外部函數的變量本身改變值的話,內部函數也會跟着改變,這個是比較危險的一個行爲。

5、操做符

as 和 is

as 表示直接強轉類型,可能會拋出異常。

(name as String) = 'bb';
複製代碼

is 表示判斷類型:

if(name is String){
	// 自動強轉
  name = 'bb';
}
複製代碼

? ?? ?.

二元表達式: condition ? expr1 : expr2,若是 condition 爲true,則執行 expr1,不然執行 expr2;

expr1 ?? expr2,若是 expr1 爲 null,則返回 expr2, 不然返回它本身。

foo?.bar 這裏的語法和 kotlin 同樣,foo不爲 null 纔會訪問 bar。

級聯

通常給對象實例變量或方法賦值時,會相似於 test().x = true 這樣的形式,若是採起 test()..x = true,賦值動做是沒有不一樣的,可是返回值爲實例對象自己,所以能夠鏈式調用起來了, 看起來很是的乾淨整潔。

test()
	..x = true
	..y = "hello";
複製代碼

6、異常

try {
  breedMoreLlamas();
} on OutOfLlamasException {
  // 一個指定的異常
  buyMoreLlamas();
} on Exception catch (e) {
  // 任何異常
  print('Unknown exception: $e');
} catch (e) {
  // 任何異常
  print('Something really unknown: $e');
}finally {
  // 不管有沒有異常都會走到finally.
}

複製代碼

on 關鍵字用來指定特定的異常,catch 關鍵字是捕獲異常對象。二者也能夠一塊兒使用。

7、類

1. factory

使用 factory 關鍵字修飾構造函數通常有兩種用途:

  • 簡單工廠模式,經過傳 type 返回對應的子類型。

    abstract class Animal {
      
      Animal._();
    
      factory Animal(String type){
        if("dog" == type){
          return new Dog();
        }else {
          return new Cat();
        }
      }
    }
    
    class Dog extends Animal {
      Dog(): super._(){}
    }
    
    class Cat extends Animal {
      Cat(): super._(){}
    }
    複製代碼
  • 單例模式:

    class Singleton {
      static Singleton _instance;
      // 禁止外部調用構造函數
      Singleton._internal(){}
    
      factory Singleton() => _getInstance();
    
      static Singleton _getInstance(){
        if(_instance == null){
          _instance = Singleton._internal();
        }
        return _instance;
      }
    }
    複製代碼

**2. getter setter **

每一個定義的變量默認都有 get 和 set 方法,若是想要自定義,則:

class Test{
  int get width => 1;
  set width(int num) => width = num;
}
複製代碼

能夠看作是一個函數表達式,只是要加上 get 和 set 關鍵字。

3. 抽象類

基本和 Java 的抽象類差很少。

4. 接口

Dart 中每一個類其實都是個接口,可使用 implements 實現這個接口,而且必須實現這個接口的變量和函數。

class Test {
  int num = 1;
  void getNum(){}
}

class Test1 implements Test {
  @override
  int num;
  
  @override
  void getNum() {
    // TODO: implement getNum
  }
}
複製代碼

5. 枚舉類

也和 Java 同樣。

6. mixin

mixin 是Dart的特點,比較實用,它主要的做用就是複用代碼。不少時候不想寫父類或者想用於更多不一樣類別的類時,mixin就是很是好的選擇。

class Test with MixinTest{
  Test(){
    this.age = 1;
  }
  void doSomeThing(){
    this.getAge();
  }
}

mixin MixinTest {
  int age = 18;
  int getAge(){
    return age;
  }
}
複製代碼

看上去和繼承很是像,其實 mixin 的做用也有爲了解決 Dart 單繼承的特性,類能夠混合多個 mixin 類就如同實現接口通常。Test 能用 MixinTest 內全部的變量和方法。不過 mixin 修飾的類不能有構造函數,另外能夠省略 MixinTest 修飾的 mixin 關鍵字,只須要用到 with 關鍵字便可。mixin 還能夠強制使用本身的類必須繼承某些類:

// Test 必須繼承Super
class Test extends Super with MixinTest{  }
// 使用 on 關鍵字
mixin MixinTest on Super{}

class Super{}
複製代碼

這種使用場景基本是 mixin 類須要某些類的支撐,才能使用。

7. static

static 修飾的變量只有在被使用到的時候纔會被初始化,這一點和 Java 有很大的差別,Java 是在類初始化的時候就會把 static 變量初始化。

8、泛型

Dart 中的泛型和 Java 中徹底同樣,也是泛型擦除,而且使用方式都是如出一轍的。

9、庫

導入庫的用法:

import 'package:lib2/lib2.dart' as lib2;  // 至關於把庫重命名,以後就使用lib2來調用庫內的函數、變量
import 'package:lib1/lib1.dart' show foo; // show 展現庫中一部分的內容,這裏只展現 foo
import 'package:greetings/hello.dart' deferred as hello; // 懶加載庫,用到的時候纔會加載
複製代碼

10、異步

Future

首先必需要理解一個前提,Dart 自己是一個單線程的模型,因此在 Dart 中執行耗時操做不能叫作併發執行(固然dart中也有相似的操做),通常來講不是很是耗時的操做,如在一秒以內的網絡請求、I/O操做之類的,通常都會採起異步的方式。而Future就是實現異步的關鍵。

print(1); 
Future future = Future(()=>"hello");
future.then((value) => print(value));
print(2);
複製代碼

這段代碼的輸出結果順序是 1 2 hello

同步意思就是按順序執行代碼,而異步就是讓其餘代碼先執行,本身在它們執行完了以後再執行。()=>"hello" 這個函數徹底能夠用耗時操做代替,它會被加入到隊列中,等待同步代碼執行完後執行。但要知道的是主線程的總耗時是不變的,只是換個順序罷了,因此很是耗時的最好仍是不要用異步。

async await

async await 實際上是個語法糖,其本質依然是 Future,只是它們能把異步當作同步代碼來看,能讓人更容易理解。

void test async {
  print(1); 
	hello();
	print(2);
}
Future<void> hello() async{
   return await Future(() => print("hello"));
}
複製代碼

其實看起來並無更容易理解。。。

Stream

異步事件流,即響應式編程。概念比較繞,**就我如今的理解就是異步去執行某個操做後最後會回調到監聽函數中,而後監聽函數可能又會作出相應的操做,而後又回調到流訂閱的另外一個監聽函數中,以鏈式調用的形式按順序執行一系列事件卻不影響在其以後的代碼的執行。**Future 只關心一個異步操做而後回調結果,而Stream就是增強版的Future,它能夠接受觸摸事件、數據流等任何事件。

var transformer = StreamTransformer<int,String>.fromHandlers(
  handleData: (data, sink){
    sink.add((data + 1).toString());
  }
);
StreamController<int> controller = StreamController();
controller.stream
    .transform(transformer)
    .listen((data) => {
      print("result = " + data.toString())
    });
controller.sink.add(3);
controller.sink.add(1);
複製代碼

以上的示例代碼中 controller.stream 就是一個流,每當有數據發送到流中時即 add(),中間能夠通過不少層的調用,好比轉換了一次數據,而後最後達到監聽函數。

也能夠用 async, await for 關鍵字實現stream看起來像同步代碼那樣執行:

controller.sink.add(3);
controller.sink.add(1);
testStream(controller);

testStream(StreamController controller) async {
  await for(var item in controller.stream){
    print("testStream = " + item.toString());
  }
}
複製代碼

我的感受仍是回調看着舒服些。。推薦一篇 Stream

11、生成器

生成器顧名思義就是生成了某個東西。其實 async await 也算是一個生成器,即返回的值並非最終的返回值,async 函數若是你返回了 String 類型的值,那麼它的真實返回結果實際上是 Future<String>。如今生成器有幾個關鍵字:sync*, async*, yield, yield*。

sync* 表示同步生成器,其返回值是一個迭代器 Iterable, 能夠經過迭代器遍歷:

void main(){  
  Iterator it = test(5).iterator;
  print(test(5));
  while(it.moveNext()){
    print(it.current);
  }
}

Iterable<int> test(int num) sync*{
  while(num > 0){
    yield num;
    num --;
  }
}
複製代碼

這個貌似挺好理解的。不過 async* 異步生成器返回的是 Stream,這裏就要提到Stream生成的幾種方式:

  • StreamController() 能夠經過生成的 controller.stream 獲取流。
  • 生成器 async, yield 關鍵字生成 Stream。
  • Stream.fromFuture 從 Future 建立新的單訂閱流,當 future 完成時將觸發一個 data 或者 error,而後使用Down事件關閉這個流。
  • Stream.fromFutures 從一組Future建立一個單訂閱流,每一個future都有本身的data或者error事件,當整個Futures完成後,流將會關閉。若是Futures爲空,流將會馬上關閉。
  • Stream.fromIterable 建立從一個集合中獲取其數據的單訂閱流。

12、Isolate

在上文有提到過異步只是調換了執行代碼的順序,不是在Java中理解的併發概念,在 Dart 中若是想作到併發那就要使用 Isolate 了。能夠把它理解爲微線程或協程,它不是一個線程。咱們平時的代碼是執行在主Isolate中的,多個 Isolate 是不共享內存的,能夠經過特定的方式進行通訊。這裏不會詳細說明它的用法。

十3、元數據

元數據即註解,在 dart 中能夠直接把一個類做爲註解,以下:

class Todo {
  final String who;
  final String what;

  const Todo(this.who, this.what);
}
// 使用
@Todo("1","2")
var test(){}
複製代碼

而後用反射獲取註解便可。

相關文章
相關標籤/搜索