Dart 型別測試運算子

三種運算子總覽(as / is / is!)

運算子功能說明
as型別強制轉換;亦可用於程式庫前綴別名限定
is型別判斷:物件屬於指定型別時回傳 true
is!型別判斷:物件不屬於指定型別時回傳 true

obj is T 成立條件:物件 obj 實作 / 繼承型別 T(包含父類別、介面、可空型別)。

舉例來說 任意物件 is Object? 永遠為 true

is 運算子

安全型別判斷

判斷物件執行期型別;若判斷結果為 true,程式區塊內可直接以該型別操作,不需手動強制轉型;若物件為 null 或型別不符,直接回傳 false,不會拋出例外。

下方範例使用類別物件,讀者若尚未學到類別,可先將其理解為用來封裝資料的資料模型。

class Person {
  String firstName = '';
}

class Employee extends Person {}

void main() {
  dynamic employee = Employee();

  // 使用 is 判斷型別
  if (employee is Person) {
    // 進入判斷分支後,employee 自動推斷為 Person 型別
    employee.firstName = "Jack";
    print(employee.firstName); // 輸出 Jack
  }

  dynamic emptyObj = null;
  if (emptyObj is Person) {
    // 不會執行,null 不匹配 Person 型別
    print("比對成功");
  }
}Code language: Dart (dart)

以上程式定義 Person 類別,再建立 Employee 類別並繼承 Person。

程式中透過 is 判斷物件是否為 Person,若成立,在大括號區塊內就能直接當作 Person 操作。此處變數採用 dynamic 動態型別宣告:dynamic employee = Employee();

第二個範例 dynamic emptyObj = null;,因為變數值為 null,因此 emptyObj is Person 結果為 false,大括號內程式不會執行。

is! 運算子

反向型別判斷

語法:

變數 is! 型別

實際等同於先用 is 判斷後再取反。

等價寫法:!(變數 is 型別),用來判斷物件不屬於目標型別。

參考範例:

class Person {
  String name = '';
}

void main() {
  dynamic obj = "文字";
  if (obj is! Person) {
    print("obj 不是 Person 型別");
  }
}Code language: Dart (dart)

上述範例會執行大括號內程式,輸出「obj 不是 Person 型別」,因為 obj 並非透過 Person 類別建立的實體。

as 運算子

強制型別轉換

使用方式:

(變數 as 目標型別).屬性/方法Code language: JavaScript (javascript)

僅建議在你百分之百確認物件就是目標型別時使用,否則會拋出執行期例外。為維持程式穩健性,不建議大量使用。

class Teacher {
  String name = '';
}

void main() {
  dynamic jason = Teacher();
  // 強制轉型為 Teacher 後存取屬性
  (jason as Teacher).name = "Jason";
  print((jason as Teacher).name); // Jason
}Code language: Dart (dart)

以上範例輸出 Jason

我們透過 dynamic jason = Teacher(); 建立 Teacher 實體,能完全確認該物件型別為 Teacher,因此這段轉換不會報錯。

若像下方這樣撰寫,就會發生錯誤。

class Teacher {
  String name = '';
}

class Car {
  String name = '';
}

void main() {
  dynamic jason = Teacher();
  
  // 強制轉型為 Car 後存取屬性
  (jason as Car).name = "Jason";//❌錯誤
  print((jason as Car).name); //❌錯誤
}Code language: Dart (dart)

因為 jason 本身是 Teacher 實體,卻強制轉換為 Car,執行時會報錯。

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Unhandled exception:
type 'Teacher' is not a subtype of type 'Car' in type cast
#0      main (file:///D:/dartdemo/firstdart/bin/firstdart.dart:12:10)
#1      _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:314:19)
#2      _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:193:12)Code language: PHP (php)

asis 核心差異比較

// 寫法1:as 強制轉型
(employee as Person).firstName = 'Bob';

// 寫法2:先用 is 判斷再操作
if (employee is Person) {
  employee.firstName = 'Bob';
}Code language: JavaScript (javascript)

employeenull 或不屬於 Person 型別時:

  • as 寫法:直接拋出 CastError,程式中斷;
  • is 寫法:不會進入 if 區塊,程式無錯誤、不執行任何操作。

因此優先使用 is 判斷的寫法,雖然程式碼會多幾行,但程式穩健度更高。

as 的第二種用途

as 的第二種用途

as 除了型別轉換,匯入程式庫時還能替程式庫設定別名

import 'dart:math' as math;

void main() {
  // 透過程式庫別名呼叫類別/方法
  print(math.pi); //輸出 3.141592653589793
}Code language: JavaScript (javascript)

完整綜合範例

class Animal {}
class Cat extends Animal {}

void main() {
  dynamic obj = Cat();

  // 1. is 正向判斷
  if (obj is Animal) {
    print("obj 是 Animal 型別");
  }

  // 2. is! 反向判斷
  if (obj is! String) {
    print("obj 不是字串");
  }

  // 3. as 強制轉型
  Animal a = obj as Animal;

  // 錯誤示範,執行會崩潰
  dynamic str = "hello";
  // Animal error = str as Animal; // CastError
}Code language: JavaScript (javascript)

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *