Flutter跨平臺(tái)開發(fā)-語法基礎(chǔ)

Dart For Objcer

本文檔是一個(gè)面向Objcer 的更加精煉的 Dart 語言概覽,刪除了相同的語法概念,僅剩差異部分,或 dart 獨(dú)有的部分。未提及的,均可以沿用 objc 經(jīng)驗(yàn)。

數(shù)據(jù)類型

所有數(shù)據(jù)類型都繼承自 Object類。(包括int、String、函數(shù)以及 null 等等)。All is Object.

常用內(nèi)置數(shù)據(jù)類型:

  • num
    • int
    • double
  • String
  • bool (true | false)
  • List ,數(shù)組
  • Set,集合
  • Map,字典

變量 & 常量

變量

dart 默認(rèn)聲明的為變量,變量聲明主要有以下幾種形式:

  • 類型約定:

    String name; // 默認(rèn)值為 null:未初始化的所有變量擁有一個(gè)默認(rèn)的初始化值:null,包括 num。
    name = 'Bob';
    
    int age = 18;
    
  • 類型推斷,通過 關(guān)鍵詞 var 來聲明

    var name = 'Bob'; //name is String 
    name = 123; //編譯報(bào)錯(cuò)
    
  • 使用動(dòng)態(tài)類型,通過 關(guān)鍵詞 dynamic 來聲明, 類似 OC 中的 id

    dynamic name = 'Bob'; //name is String 
    name = 123; //name is int
    

一般情況下,我們使用 var 來聲明變量,讓編譯器幫我們推斷其類型;

常量

常量聲明涉及到兩個(gè)關(guān)鍵字 finalconst。使用示例如下:

final String name; // 編譯報(bào)錯(cuò):未初始化
final String name = 'dann'; //?
name = 'dann2'; //編譯報(bào)錯(cuò),提示僅能賦值一次

const String name; // 編譯報(bào)錯(cuò):未初始化
const String name = 'dann'; //?
name = 'dann2'; //編譯報(bào)錯(cuò),常量變量不能被賦值
  • final 用于聲明==運(yùn)行時(shí)==常量;
  • const 用于聲明==編譯時(shí)==常量;

示例說明如下:

final abc = 1 + 2;  //?
const bb = 1 + 2;       //?

final x = DateTime.now(); // ?
const x = DateTime.now(); // ?

函數(shù)

一般函數(shù)形式:

bool isNoble(int atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}

//函數(shù)體只包含單個(gè)表達(dá)式的簡寫,同上
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null; 

函數(shù)參數(shù)分為兩種:

  • 必要參數(shù)

    bool isNoble(int atomicNumber/* 必要參數(shù) */){
      ...
    }
    
  • 可選參數(shù):一個(gè)函數(shù)==僅可==包含一種可選參數(shù)

    • 命名參數(shù):{type param1, type param2, ...}

      bool isNoble(int atomicNumber/* 必要參數(shù) */, {bool bold = false, @required Widget child}){
      ...
      }
      
    • 位置參數(shù):[type param1, type param2, ...],

      bool isNoble(int atomicNumber/* 必要參數(shù) */, [bool bold]){
      ...
      }
      

函數(shù)注意點(diǎn):

  • 必要在前,可選在后;

  • 可對(duì)參數(shù)設(shè)置默認(rèn)值 isNoble(int atomicNumber = 110)

  • 即使是可選參數(shù),也可通過關(guān)鍵字 @required 將其約束為 必要的(調(diào)用函數(shù)時(shí),必須對(duì)該參數(shù)賦值)

  • 函數(shù)也是對(duì)象,可作為參數(shù)傳遞

    void printElement(int element) {
      print(element);
    }
    
    var list = [1, 2, 3];
    
    // 將 printElement 函數(shù)作為參數(shù)傳遞。
    list.forEach(printElement);
    

匿名函數(shù)

類似 OC 中的 Block

void main() {
var list = ['apples', 'bananas', 'oranges'];
// forEach(void Function(String) f) -> void
list.forEach((item){
  print(item);
 });
}

表達(dá)式

表達(dá)式基本繼承自類 C 語言,OC中沒有的表達(dá)式,有如下幾個(gè):

  • ~/ 除并取整

    print(5/2 == 2.5);  //true, 此處不同于 OC ,返回整數(shù)
    print(5 ~/ 2 == 2); //true
    
  • as 類型轉(zhuǎn)換 | is is! 類型判斷

    // 類型檢查
    if (emp is Person) {
     emp.firstName = 'Bob';
    }
    
    // 類型檢查
    if (emp is! Person) {
     //TODO: 
    }
    
    //此時(shí)要確保 emp 是 Person 類型,否則會(huì)拋出異常
    (emp as Person).firstName = 'Bob'; 
    
  • .. 級(jí)聯(lián) 在 同一個(gè)對(duì)象 上連續(xù) 調(diào)用 多個(gè)對(duì)象的變量或方法。

    querySelector('#confirm') // 獲取對(duì)象 (Get an object).
      ..text = 'Confirm' // 使用對(duì)象的成員 (Use its members).
      ..classes.add('important')
      ..onClick.listen((e) => window.alert('Confirmed!'));
    
    //equal to
    var button = querySelector('#confirm');
    button.text = 'Confirm';
    button.classes.add('important');
    button.onClick.listen((e) => window.alert('Confirmed!'));
    
    //返回值為 void 的方法則不能使用級(jí)聯(lián)運(yùn)算符
    var sb = StringBuffer();
    sb.write('foo')
      ..write('bar'); // 出錯(cuò):void 對(duì)象中沒有方法 write
    
  • ?? 空判斷 expr1 ?? expr2 expr1 非空,則返回其值,否則執(zhí)行 expr2 并返回值

      var num;
      print(num??123); // 123
      num = 456;
      print(num??123); // 456
    
  • ??= 空則賦值

      var a = 123;
      a ??= 456;
      print(a); //123
      
      var b;
      b ??= 456;
      print(b); //456
    
  • ?. 條件訪問成員

      List numbs;
      print(numbs?.length); // null
      numbs = [1, 2, 3];
      print(numbs?.length); // 3
    

流程控制

流程控制語句也基本繼承自 C 語言;按已有 OC 習(xí)慣使用即可;

異常

關(guān)鍵字:

  • throw
  • try
  • catch
  • finally

流程及使用習(xí)慣,類 Java,如下:

void breedMoreLlamas(){
  if(1){
    //TODO: 
  }else{
    throw 'Out of llamas!';
  }
}

try {
  breedMoreLlamas();
} catch (e) {
  print('Error: $e'); // 先處理異常。
} finally {
  cleanLlamaStalls(); // 然后清理。
}

  • 一個(gè)類只有一個(gè)父類;
  • 類可擴(kuò)展,同OC;

成員使用

var p = Point(2, 2);

// 為實(shí)例變量 y 賦值。
p.y = 3;

// 獲取 y 的值。
assert(p.y == 3);

// 調(diào)用變量 p 的 distanceTo() 方法。
num distance = p.distanceTo(Point(4, 4));

構(gòu)造函數(shù)

class Point {
  num x, y;

  Point(num x, num y) { 
    this.x = x; //使用 this 關(guān)鍵字引用當(dāng)前實(shí)例; OC 中的self
    this.y = y;
  }
  
  // 語法糖: 在構(gòu)造函數(shù)體執(zhí)行前用于設(shè)置 x 和 y 的, equal to above
  Point(this.x, this.y);
}

var p1 = Point(2, 2);
var p2 = Point.fromJson({'x': 1, 'y': 2});

// new 關(guān)鍵字可選
var p1 = new Point(2, 2);
var p2 = new Point.fromJson({'x': 1, 'y': 2});

常量構(gòu)造函數(shù)

兩個(gè)使用相同構(gòu)造函數(shù)相同參數(shù)值構(gòu)造的編譯時(shí)常量是同一個(gè)對(duì)象:

var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);

assert(identical(a, b)); // 同一個(gè)實(shí)例!

命名式構(gòu)造函數(shù)

  • 聲明一個(gè)類多個(gè)命名式構(gòu)造函數(shù)來表達(dá)更明確的意圖;
  • 不能被繼承;
class Point {
  num x, y;

  Point(this.x, this.y);

  // 命名式構(gòu)造函數(shù)
  Point.origin() {
    x = 0;
    y = 0;
  }
}

初始化列表

除了調(diào)用父類構(gòu)造函數(shù)之外,還可以在構(gòu)造函數(shù)體執(zhí)行之前初始化實(shí)例變量。每個(gè)實(shí)例變量之間使用逗號(hào)分隔。

// Initializer list sets instance variables before
// the constructor body runs.
// 使用初始化列表在構(gòu)造函數(shù)體執(zhí)行前設(shè)置實(shí)例變量。
Point.fromJson(Map<String, num> json)
    : x = json['x'],
      y = json['y'] {
  print('In Point.fromJson(): ($x, $y)');
}

其他構(gòu)造函數(shù)

詳情參見Dart API 文檔:

  • 重定向構(gòu)造函數(shù) :類似OC 中的 非指定構(gòu)造函數(shù)
  • 工廠構(gòu)造函數(shù):關(guān)鍵字 factory, 使用該構(gòu)造函數(shù)構(gòu)造類的實(shí)例時(shí)==并非==總是會(huì)返回新的實(shí)例對(duì)象。例如,工廠構(gòu)造函數(shù)可能會(huì)從緩存中返回一個(gè)實(shí)例,或者返回一個(gè)子類型的實(shí)例。==工廠中不可以訪問 this==

獲取對(duì)象類型

關(guān)鍵字 runtimeType

print('The type of a is ${a.runtimeType}');

方法 & 屬性

使用同OC, 但使用 . 語法

每個(gè)屬性都有 Getter 方法, 對(duì)于 非 final 屬性,還有 Setter 方法。 可以通過 關(guān)鍵字 getset 為額外的屬性(如計(jì)算屬性),添加 Getter 和 Setter 方法:

class Rectangle {
  num left, top, width, height;

  Rectangle(this.left, this.top, this.width, this.height);

  // 定義兩個(gè)計(jì)算產(chǎn)生的屬性:right 和 bottom。
  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);
}

抽象類

使用關(guān)鍵字 abstract 標(biāo)識(shí)類可以讓該類成為 抽象類,抽象類將無法被實(shí)例化。抽象類常用于聲明接口方法、有時(shí)也會(huì)有具體的方法實(shí)現(xiàn)。如果想讓抽象類同時(shí)可被實(shí)例化,可以為其定義工廠構(gòu)造函數(shù)。

abstract class Doer {
  // 定義實(shí)例變量和方法等等……

  void doSomething(); // 定義一個(gè)抽象方法。
}

class EffectiveDoer extends Doer {
  void doSomething() {
    // 提供一個(gè)實(shí)現(xiàn),所以在這里該方法不再是抽象的……
  }
}

隱式接口

騷操作

接口 即 OC 中的協(xié)議。

==每一個(gè)類都隱式地定義了一個(gè)接口并實(shí)現(xiàn)了該接口,這個(gè)接口包含所有這個(gè)類的實(shí)例成員以及這個(gè)類所實(shí)現(xiàn)的其它接口。如果想要?jiǎng)?chuàng)建一個(gè) A 類支持調(diào)用 B 類的 API 且不想繼承 B 類,則可以實(shí)現(xiàn) B 類的接口。==

一個(gè)類可以通過關(guān)鍵字 implements 來實(shí)現(xiàn)一個(gè)或多個(gè)接口并實(shí)現(xiàn)每個(gè)接口定義的 API:

// Person 類的隱式接口中包含 greet() 方法。
class Person {
  // _name 變量同樣包含在接口中,但它只是庫內(nèi)可見的。
  final _name;

  // 構(gòu)造函數(shù)不在接口中。
  Person(this._name);

  // greet() 方法在接口中。
  String greet(String who) => '你好,$who。我是$_name。';
}

// Person 接口的一個(gè)實(shí)現(xiàn)。
class Impostor implements Person {
  get _name => '';

  String greet(String who) => '你好$who。你知道我是誰嗎?';
}

String greetBob(Person person) => person.greet('小芳');

void main() {
  print(greetBob(Person('小蕓')));
  print(greetBob(Impostor()));
}

實(shí)現(xiàn)多個(gè)接口,使用逗號(hào)分隔:

class Point implements Comparable, Location {...}

擴(kuò)展

子類化

使用 extends 關(guān)鍵字來創(chuàng)建一個(gè)子類,并可使用 super 關(guān)鍵字引用一個(gè)父類:

class TV{
  void turnOn() {
    _illuminateDisplay();
    _activateIrSensor();
  }
  // ···
}

class SmartTV extends TV {
  void turnOn() {
    super.turnOn();
    _bootNetworkInterface();
    _initializeMemory();
    _upgradeApps();
  }
  // ···
}
重寫

子類可以重寫父類的實(shí)例方法、Getter 以及 Setter 方法。使用 @override 來標(biāo)示:

class SmartTV extends TV {
  @override
  void turnOn() {...}
  // ···
}

擴(kuò)展方法

一種向現(xiàn)有庫添加功能的方式。extension extensionName on ClassName{...}

extension NumberParsing on String {
  int parseInt() {
    return int.parse(this);
  }
}

print('42'.padLeft(5)); // 使用 原始類 自帶方法
print('42'.parseInt()); // 使用 擴(kuò)展方法

Mixin

一種在多重繼承中復(fù)用某個(gè)類中代碼的方法模式。

定義一個(gè)類繼承自 Object 并且不為該類定義構(gòu)造函數(shù),這個(gè)類就是 Mixin 類,除非你想讓該類與普通的類一樣可以被正常地使用,否則可以使用關(guān)鍵字 mixin 替代 class 讓其成為一個(gè)單純的 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 關(guān)鍵字并在其后跟上 Mixin 類的名字來使用 Mixin 模式:

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

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

使用關(guān)鍵字 on 來指定哪些類可以使用該 Mixin 類,比如有 Mixin 類 A,但是 A 只能被 B 類使用,則可以這樣定義 A:

mixin MusicalPerformer on Musician {
  // ···
}

枚舉

枚舉具有較大的局限性,其值不可自定義。

每一個(gè)枚舉值都有一個(gè)名為 index 成員變量的 Getter 方法,該方法將會(huì)返回以 0 為基準(zhǔn)索引的位置值。例如,第一個(gè)枚舉值的索引是 0 ,第二個(gè)枚舉值的索引是 1。以此類推。

enum Color { red, green, blue }

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

類變量及類方法

使用 static 修飾

類成員
class Queue {
  static const initialCapacity = 16;
  // ···
}

void main() {
  assert(Queue.initialCapacity == 16);
}

靜態(tài)變量在其首次被使用的時(shí)候才被初始化。

類方法
import 'dart:math';

class Point {
  num x, y;
  Point(this.x, this.y);

  static num distanceBetween(Point a, Point b) {
    var dx = a.x - b.x;
    var dy = a.y - b.y;
    return sqrt(dx * dx + dy * dy);
  }
}

void main() {
  var a = Point(2, 2);
  var b = Point(4, 4);
  var distance = Point.distanceBetween(a, b);
  assert(2.8 < distance && distance < 2.9);
  print(distance);
}

泛型

如今的每個(gè)現(xiàn)代編程語言都支持泛型。日常使用中,最常見于集合。常用于需要要求類型安全的情況,好處還有:

  • 適當(dāng)?shù)刂付ǚ盒涂梢愿玫貛椭a生成。
  • 使用泛型可以減少代碼重復(fù)。
var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
names.add(42); // Error

在上述代碼中,為集合指定類型為 String,方便編譯器檢查,避免插入錯(cuò)誤類型數(shù)據(jù);

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

在上述代碼中,T 是一個(gè)替代類型。其相當(dāng)于類型占位符,在開發(fā)者調(diào)用該接口的時(shí)候會(huì)指定具體類型。

泛型約束

可能會(huì)想限制泛型的類型范圍,這時(shí)候可以使用 extends 關(guān)鍵字:

class Foo<T extends SomeBaseClass> {
  // 具體實(shí)現(xiàn)……
  String toString() => "'Foo<$T>' 的實(shí)例";
}

class Extender extends SomeBaseClass {...}

//這時(shí)候就可以使用 SomeBaseClass 或者它的子類來作為泛型參數(shù):
var someBaseClassFoo = Foo<SomeBaseClass>();
var extenderFoo = Foo<Extender>();

泛型方法

在方法中,使用泛型

T first<T>(List<T> ts) {
  // 處理一些初始化工作或錯(cuò)誤檢測(cè)……
  T tmp = ts[0];
  // 處理一些額外的檢查……
  return tmp;
}

異步

不同于 OC 中 通過 GCD 和 NSOperation 來支持異步編程。

Dart 中 使用 Futureawait/async 來支持異步編程。

Future

Future 基本使用上類似于 GCD,如下:

void main() { 
Future(() => print('立刻在Event queue中運(yùn)行的Future'));
  
Future.delayed(const Duration(seconds:1), () => print('1秒后在Event queue中運(yùn)行的Future'));
  
Future.microtask(() => print('在Microtask queue里運(yùn)行的Future'));
  
Future.sync(() => print('同步運(yùn)行的Future'));
  
Future(()=> print('task'))
  .then((_)=> print('callback1'))
  .then((_)=> print('callback2'))
  .catchError((error)=>print('$error'))
  .whenComplete(()=> print('whenComplete'));
}

//print as follow:
/*
同步運(yùn)行的Future
在Microtask queue里運(yùn)行的Future
立刻在Event queue中運(yùn)行的Future
task
callback1
callback2
whenComplete
1秒后在Event queue中運(yùn)行的Future
*/

其中Dart的事件循環(huán),類似 OC 中的 Runloop, 大概如下:

1、Dart的入口是main函數(shù),所以main函數(shù)中的代碼會(huì)優(yōu)先執(zhí)行;

2、main函數(shù)執(zhí)行完后,會(huì)啟動(dòng)一個(gè)事件循環(huán)(Event Loop)就會(huì)啟動(dòng),啟動(dòng)后開始執(zhí)行隊(duì)列中的任務(wù);

3、首先,會(huì)按照先進(jìn)先出的順序,執(zhí)行 微任務(wù)隊(duì)列(Microtask Queue)中的所有任務(wù);

4、其次,會(huì)按照先進(jìn)先出的順序,執(zhí)行 事件隊(duì)列(Event Queue)中的所有任務(wù);

為了避免 地獄回調(diào) 的問題,dart 又通過引入 await/async 來實(shí)現(xiàn)以同步的代碼風(fēng)格,實(shí)現(xiàn)異步調(diào)用。

await/async

Future<String> getNetworkData() async {
  var result = await Future.delayed(Duration(seconds: 3), () {
    return "network data";
  });

  return  "請(qǐng)求到的數(shù)據(jù):" + result;
}

main() async {
  print('print network data...');
  print(await getNetworkData());
}

其他

  • isolates dart 中獨(dú)有的概念,用于代替線程使用,詳情參見官網(wǎng);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容