Dart語法基礎

Bozo發表於2019-03-08

變數

變數的宣告以及賦值:

//如果不初始化,則後續可以賦值任意型別,最終型別是dynamic型別,dynamic型別不會檢測成員
var name = "Bozo";
複製程式碼

在宣告變數的時候,也可以選擇使用具體的資料型別:

//使用確定型別定義變數,賦值必須是相同型別
String name = "bozo";
int age = 12;
double height = 176.3;
bool onLine = true;
複製程式碼

沒有初始化變數預設值都為null,即使變數型別為bool或者int

Dart中的基本資料型別:numdoubleintStringListSetStringmapboolrunesSymbolObjectdynamic

Dart中所有資料型別都是物件,都繼承自Object類,null也繼承自Object

const&final

使用constfinal定義的變數都只能賦值一次,並且在宣告的時候必須進行初始化。

使用const定義的集合都是隻讀的,特性會傳遞到子元素,在記憶體中會複用相同內容物件,而final則不會作用於子元素,在記憶體中也不會複用相同物件

//不可以通過 list[0] = 5; 來修改集合中元素的值, 如果換做final來定義則可以修改
const list = [1,2,3,4];
const list2 = [1,2,3,4];
//此時輸出的結果為 true, 如果集合是用final定義的則輸出結果為 false
print(identical(list, list2));
複製程式碼

例項變數可以為final但是不能是const

方法

方法可以賦值給變數,也可以當作其他方法的引數,以下為定義方法的示例:

bool isEmpty(String str){
  return str == null || str == "";
}

//可以忽略型別定義
isEmpty(str){
  return str == null || str == "";
}

//如果是隻有一個表示式的方法,則可以使用縮寫語法來定義
isEmpty(str) => str == null || str == "";
複製程式碼

在Dart中方法還可以設定可選引數,以及設定預設值

void main() {
  printInfo("Bozo", 13, true);
  //可選位置引數
//  printInfo("Bozo", false);
  //可選命名引數
//  printInfo(name: "Bozo", showAge: false);
  //指定預設值
//  printInfo(name: "Bozo", showAge: true);
}

printInfo(name, age, showAge) {
  if (showAge) {
    print("name:$name,age:$age");
  } else {
    print("name:$name");
  }
}

//可選位置引數
printInfo(name, showAge, [age]) {
  ......
}

//可選命名引數
printInfo({name, age, showAge}) {
  ......
}

//指定預設值
printInfo({name, age = 12, showAge}) {
  ......
}
複製程式碼

所有函式都有一個返回值,如果沒有指定返回值,則預設把return null;作為函式得最後一個語句執行

操作符

Dart語法基礎

操作符過載

以下操作符可以被過載

Dart語法基礎

例如重寫+-操作符

class Vector{
  final int x;
  final int y;

  Vector(this.x, this.y);

  Vector operator +(Vector v){
    return Vector(x + v.x, y + v.y);
  }

  Vector operator -(Vector v){
    return Vector(x - v.x, y - v.y);
  }
}

void main(){
  Vector v1 = Vector(4, 3);
  Vector v2 = Vector(2, 1);

  Vector v3 = v1 - v2;
  Vector v4 = v1 + v3;

  //print: v3:x=2,y:2
  print("v3:x=${v3.x.toString()},y:${v3.y.toString()}");
  //print: v4:x=6,y:5
  print("v4:x=${v4.x.toString()},y:${v4.y.toString()}");
}
複製程式碼

如果覆寫的操作符是==,則還應覆寫物件的hashCode getter 函式

型別判定操作符

asisis!操作符是在執行時判定物件型別的操作符

操作符 解釋
as 型別轉換
is 如果物件是指定的型別返回true
is! 如果物件是指定的型別返回false
級聯操作符

級聯操作符 (..) 可以在同一個物件上 連續呼叫多個函式以及訪問成員變數,使用級聯操作符可以避免建立 臨時變數

void main() {
  Person()
    ..name = 'bozo'
    ..age = 12
    ..contact = (new Contact()
      ..phoneNumber = '126854315'
      ..email = 'bozo@gmail.com')
    ..showInfo();
}

class Person {
  String name;
  int age;
  Contact contact;

  showInfo() {
  print("Name:${this.name},Age:${this.age},PhoneNumber:${this.contact.phoneNumber},Email:${this.contact.email}");
  }
}

class Contact {
  String phoneNumber;
  String email;
}
複製程式碼

異常

丟擲異常
throw new FormatException('Expected at least 1 section!');

//還可以丟擲任意物件
throw 'Out of llamas!'
複製程式碼
捕獲異常
try {
    throwExceptionsFuc();
  } on FormatException {
    ...
  } on Exception catch (e) {
    ...
  } catch (e) {
    ...
  } finally {
    ...
  }
}
複製程式碼

可以使用on或者catch來宣告捕獲語句,也可以同時使用。使用on來指定異常型別,使用catch來捕獲異常物件,函式catch可以有一個或兩個引數,第一個引數為丟擲的異常物件,第二個為堆疊資訊

還可以使用rethrow關鍵字來把捕獲的異常重新丟擲

try{
    testFuc();
} catch (e) {
    ...
    //重新丟擲異常
    rethrow;
}
複製程式碼

Dart中每個物件都是一個類的例項,所有類都繼承於Object

建構函式

在Dart中可以使用this關鍵字來簡化建構函式的實現:

class Person{
    String name;
    int age;
    
    //原始寫法
    Person(String name, int age){
        this.name = name;
        this.age = age;
    }
    //使用this關鍵字簡化寫法
    Person(this.name, this.age);
}
複製程式碼

如果沒有定義建構函式,跟Java中一樣會有一個預設的無參構造

使用命名建構函式可以為一個類實現多個建構函式,如果要在一個建構函式中呼叫其他建構函式則使用重定向建構函式

void main() {
  Person person1 = new Person("Bozo", 17);
  Map map = new Map();
  map['name'] = "bozo";
  map['age'] = 18;
  Person person2 = new Person.fromMap(map);
}

class Person {
  String name;
  int age;

  Person(this.name, this.age);

  //命名建構函式
  Person.fromMap(Map map) {
    this.name = map['name'];
    this.age = map['age'];
  }
  
  //重定向建構函式
  Person.byName(name) : this(name, 0);
}
複製程式碼

如果要類提供一個狀態不變的物件,則可以把物件定義為編譯時常量。需要定義一個const建構函式,並且宣告所有的變數為final。使用常量構造函建立的物件具有複用性,前提是物件的成員變數值相同。

class Car {
  final String color;

  const Car(this.color);

  static final Car car = const Car("red");
}

void main(){
    Car car1 = const Car("red");
    Car car2 = const Car("black");
    
    //斷言執行通過,car1跟car為同一個物件
    assert(identical(car1, Car.car));
    //斷言執行失敗
    assert(identical(car2, Car.car));
}
複製程式碼

如果一個建構函式不是總是返回一個新的物件,則可以使用factory關鍵字來建立工廠方法建構函式

class Logger {
  final String name;
  bool mute = false;

  // _cache is library-private, thanks to the _ in front
  // of its name.
  static final Map<String, Logger> _cache =
      <String, Logger>{};

  factory Logger(String name) {
    if (_cache.containsKey(name)) {
      return _cache[name];
    } else {
      final logger = new Logger._internal(name);
      _cache[name] = logger;
      return logger;
    }
  }

  Logger._internal(this.name);

  void log(String msg) {
    if (!mute) {
      print(msg);
    }
  }
}

void main(){
    var logger = new Logger('UI');
	logger.log('Button clicked');
}
複製程式碼

工廠方法無法訪問this

extends 跟 implements
  • extends :繼承其他類的屬性以及方法,在dart中可以使用with關鍵字來實現多繼承

    class A extends B with C,D{
        ...
    }
    複製程式碼
  • implements:一個類可以通過 implements 關鍵字來實現一個或者多個介面, 並實現每個介面定義的 API

列舉

使用 enum 關鍵字來定義列舉型別,列舉中每個值都有一個index的屬性(從0開始)。列舉的values常量可以返回所有的列舉值

enum Color{
    RED,
    GREEN,
    BLUE
}

void main(){
    List<Color> colors = Color.values;
	assert(colors[2] == Color.BLUE);
}
複製程式碼

泛型

在Dart中使用泛型的方式與Java類似:

abstract class Cache<T>{
  T getByKey(String key);
  void cacheByKey(String key, T value);
}
複製程式碼

限制泛型的具體型別:

class SomeClass<T extends SomeBaseClases>{
    ...
}
複製程式碼

泛型函式:

T findByIndex<T>(List<T> data, int index) {
    return data == null ? data[index] : null;
}
複製程式碼

使用 import 來匯入庫

//內建庫使用 dart:scheme
import 'dart:io';
//其他庫使用檔案系統路徑或者 package:scheme
import 'package:libs/utils.dart';
複製程式碼

如果匯入的兩個庫具有衝突的識別符號,則可以使用庫的字首來區分,例如lib1跟lib2中都存在名為SomeClass的類:

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

...
    
SomeClass class1 = new SomeClass();
lib2.SomeClass class2 = new lib2.SomeClass();
複製程式碼

如果只想匯入庫的一部分內容可以使用 showhide

// Import only foo.
import 'package:lib1/lib1.dart' show foo;

// Import all names EXCEPT foo.
import 'package:lib2/lib2.dart' hide foo;
複製程式碼

如果要讓一個庫在使用的時候才載入則可以使用延時載入庫。如:

//使用 deferred as 匯入
import 'package:deferred/hello.dart' deferred as hello;
//當需要使用的時候,使用庫識別符號呼叫loadLibrary()函式來載入庫
hello.loadLibrary();
hello.sayHello();
複製程式碼
  • 一個庫可以呼叫 loadLibrary() 函式,但該庫只會載入一次
  • 延遲載入庫的常量在匯入的時候是不可用的。 只有當庫載入完畢的時候,庫中常量才可以使用。
  • 在匯入檔案的時候無法使用延遲庫中的型別。 如果你需要使用型別,則考慮把介面型別移動到另外一個庫中, 讓兩個庫都分別匯入這個介面庫。
  • Dart 隱含的把 loadLibrary() 函式匯入到使用 deferred as *的名稱空間* 中。 loadLibrary() 方法返回一個 Future。

非同步

非同步方法的宣告是新增 async 關鍵字,如:

...
checkVersion() async{
    ...
}
複製程式碼

使用await來等待非同步方法返回:

...
//要使用 await,其方法必須帶有 async 關鍵字
checkVersion() async{
    var version = await getVersionInfo();
    ...
}
複製程式碼