Dart 函數與方法、異常處理、引用庫 | Java 比較

Dart 函數與方法、異常處理、引用庫 | Java 比較

Overview of Content

在這篇文章中,我們將深入探討 Dart 語言中的各種函數與方法,並與 Java 方法進行對比,幫助您更好地理解這兩種語言的特點和差異。此外,還會詳細介紹 Dart 異常處理機制,包括 try-catch-finally、異常捕捉以及斷言(Assert)的使用。這些內容將為你在實際開發中提供有力的支持和指導

寫文章分享不易,如有引用參考請詳註出處,如有指導、意見歡迎留言(如果覺得寫得好也請給我一些支持),感謝 😀

個人程式分享時比較注重「縮排」,所以可能不適合手機的排版閱讀,建議切換至「電腦版」、「平板版」視窗看


Dart 函數與方法

應該稱為「方法」還是「函數」

在物件導向中,會將我們傳統所學的命令是編成得函數(function)稱之為方法(method

而在 Dart 中既有方法也有函數,因為它既可以作為物件導向設計,也可以作為命令式設計… Dart 的方法就是放置在類內的函數(這時會稱之為方法),而放置在最頂層的函數就稱之為函數


// Dart 類
public class Hello {
    
    // 宣告在 Dart 類內,稱之為「方法」
    void sayHello() {
        System.out.println("Hello~ Java method");
    }
}

// 宣告在 Dart 頂層,稱之為「函數」
void sayHello() {
    print("Hello~ Dart method")
}

Dart、Java 方法的差異

● 以下我們以 Java 這門語言的方法(Method)來與 Dart 這門語言的方法相比,看看 Dart 跟 Java 在方法的表達上差異在哪…

A. 方法的位置:Dart 多了「函數

Dart 與 Java 方法較大的差異在,Java 方法的方法就會存在類中 (class),而 Dart 中的方法可以單獨存在檔案中,這種方法稱為 頂層函數top-level function

Java 方法如下:


public class Hello {

    void sayHello() {
        System.out.println("Hello~ Java method");
    }
    
}

Dart 頂層函數如下:


void sayHello() {
    print("Hello~ Dart method");
}

B. 參數的表達

Java 方法重載機制,大多數時候是因為有多種不同的參數所需,所以需要多個相同名稱的方法(但參數需不同),範例如下… 我們可以看到,這這對於生產程式的效率上並不佳


class Hello {

    void sayHello() {
        System.out.println("Hello~ Java method");
    }

    // 為了參數方法重載
    void sayHello(String personName) {
        System.out.println("Hello~ Java method, " + personName);
    }

    // 為了參數方法重載
    void sayHello(String personName, int count) {
        for (int i = 0; i < count; i++) {
            System.out.println("Hello~ Java method, " + personName);
        }
    }

}

而在 Dart 中則是以 可選位置參數命名參數來處理,並且 Dart 的方法 允許參數有默認值

Dart 的可選位置參數、命名參數會在接下來小節會舉例說明

另外在 Dart 中並沒有方法重載機制,下圖中證明 Dart 不支援方法重載

可選位置參數:預設參數

● 在一般的方法參數設置下,呼叫方法時,方法的 1. 參數都是必須設置,並且 2. 不允許有預設參數,只要有一個條件不符合就無法通過編譯


void printMsg(String msg, int count = 1) {

  for (int i = 0; i < count; i++) {
    print("($i) $msg");
  }
  
}

如下圖所示,不使用位置、命名參數的話,就不允許有預設值

可選「位置」參數 的使用方式:

可選位置參數是 使用中括號([])包裹參數,並且它的重點在於「位置」,它會限制使用者一定要按照方法參數的位置做設置,不能指定參數名去賦予值;

範例如下


void testPosition() {
  // 全部參數使用預設值
  printInfo();

  printInfo("Alien", 998877);

  printInfo("Alien", 1111, 998877);

//  printInfo(123);     // 順序不對,前面必須要指定 name

//  printInfo(id : 123); // 無法指定
}


void printInfo([String name = "Hello", // 有預設參數
                int id = 9527,         // 有預設參數
                int? phone]) {
  print("name: $name, id: $id, phone: $phone");
}

可選命名參數:預設參數

可選「命名」參數 的使用方式:

可選位置參數是 使用花括號({})包裹參數,它與位置參數不同,使用可選命名參數時並 不在意參數的順序,因為使用它時 需要我們手動為方法的每個參數指定數值

範例如下


void testSetName() {
  // 全部參數使用預設值
  printInfo_2();

  // 按照順序命名(okay)
  printInfo_2(name: "Alien", id: 123, phone: 666666);

  // 不按照順序命名(okay)
  printInfo_2(phone: 9999, id: 777, name: "Kyle");
}

void printInfo_2({String name = "World", int id = 9527, int? phone}) {
  print("name: $name, id: $id, phone: $phone");
}

● 使用命名參數時不允許沒有指定參數名稱

一級方法物件:Function、typedef 使用

Dart 的一級方法物件特點如同 Java 的 Lambda 表達式,又如 groovy 的閉包概念,把方法作為參數傳入,在方法的內部可以依據需要隨時調用這個一級方法物件

● 在 Dart 中使用一級方法物件時常會接觸的兩個關鍵字如下表

使用關鍵字解釋
FunctionFunction 對於 Dart 來說是一種「類型」,我們所以的方法其實都是 Function 類型;但是 Function 本身不具有參數檢查功能
typedef重新命名(如同 Shell alias 的功能),配合 Function 使用時,可以在編譯期間進行 參數進行檢查

A. 使用 Function 類型:如下範例中,我們可以看到 printItemshowMsg 兩個方法雖然參數不同,但是都屬於 Function 類型


void printItem(String item) {
  print("myFunction be call, element $item");
}

void showMsg(String msg, int count) {
  for (int i = 0; i < count; i++) {
    print("($i), msg: $msg");
  }
}

// 接收 Function 類型
void receiveFunction(Function function) {
  print("Function runtimeType: ${function.runtimeType}");
}

void main() {

  receiveFunction(printItem);

  receiveFunction(showMsg);

}

image

B. 使用 typedef 定義 Function 類型:以下我們來改進 receiveFunction 方法,讓其接收指定類型的 Function


void printItem(String item) {
  print("myFunction be call, element $item");
}

void showMsg(String msg, int count) {
  for (int i = 0; i < count; i++) {
    print("($i), msg: $msg");
  }
}

// 定義 Function 類型的細節
typedef ShowItemFunc = void Function(String);

// 接收指定 Function 類型
void receiveFunction(ShowItemFunc function) {
  print("Function runtimeType: ${function.runtimeType}");
}

void main() {

  receiveFunction(printItem);

  // Error! 因為並不是所需類型的 Function
  receiveFunction(showMsg);

}

Dart 異常、斷言

Dart 異常與 Java 不同,Dart 屬於 非檢查異常1 方法不必聲明他們所拋出的異常,也就是呼叫方法時 2 不必捕捉異常

另外,Dart 提供了 ExceptionError 類型(還有一些子類),還可以自定義拋出任何的類型,甚至可以拋出方法、類 (基本上就是一切皆可拋出)

● Java 則是混合型異常,既有檢查異常(規定呼叫者一定要處理),也有非檢查異常(嚴重錯誤類型),想了解更多 Java 異常的細節,請點擊 深入理解 Java 異常處理:從基礎概念到最佳實踐指南


class JavaException {

    void nonCheckThrow() {
        throw new RuntimeException();
    }

    void checkThrow() throws IOException {
        throw new IOException();
    }

}

Dart 異常處理:try-catch-finally、不同的 catch

● 雖然 Dart 可以不捕捉異常,但是它也同樣支持 try / catch / finally 操作

Dart 與 Java 捕捉異常的不同之處在於 catch,Dart 的 catch 只有兩個預設參數 1.e(error) 拋出的異常物件、2.s(stack) 錯誤棧 可以使用


void main() {

  try {
    testException("帥到被捕捉");
  } catch(e, s) {    // 無法定義類型的參數

    print(e);
    print("runtimeType: ${e.runtimeType}\n");
    print(s);
    print("runtimeType: ${s.runtimeType}");

  } finally {
    print("被捉住哭哭");
  }
}

void testException(String str) {
  throw new Exception(str);
}

並且這兩個參數類型皆為 dynamic (可使用 runtimeType 去查看類型)

Dart 捕捉指定類型異常:on、再次拋出:rethrow

● 上面有說到 Dart 的 catch 只提供兩個參數 (並且這兩個參數皆為 dynamic 類型),那難道就不能捕捉指定類型的異常了嗎?

不,其實是可以的,但若 Dart 要捉取指定的類型就要使用 on 關鍵字(on <類型> catch(e, s)),範例如下:


void tryThrow() {
  throw UnsupportedError('This method not implement');
}

void main() {

  try {
    tryThrow();
  } on UnsupportedError catch (e) {
    print("Use `on` to catch unsupported error: ${e.message}");
  }

}

● 另外 Dart 也可以使用 rethrow 將捕捉的異常再次拋出(但這並不算是例外轉譯),範例如下…

A. 定義異常函數:


void say() {
  print("說不出的帥");
}

void testThrowFunc() {
    // 可以拋出方法
  throw say;
}

B. 使用 on 來捕捉 Function 類型的異常,並在執行過後再次透過 rethrow 把相同的異常拋


void main() {

  try {
    testThrowFunc();
  } on Function catch(e) {

    print("e: $e");
    print("e(): ${e()}");
    print("e.runtimeType: ${e.runtimeType}");

    rethrow; // 把捕獲的異常重新拋出
  } finally {
    print("- v -+");
  }

}

Dart Assert 斷言

● 在 Dart 中,assert 是一個常用的調試工具,用於在開發階段驗證程式的假設,它的主要目的是在開發過程中檢查程式的內部狀態是否符合預期,從而幫助開發者及早發現和修正錯誤(assert 通常用於檢查函數參數是否符合預期,這在函數的開發和調試過程中特別有用)

assert 的特性如下:

A. 僅在調試模式下啟用

assert 語句僅在調試模式(debug mode)下執行,在生產環境(release mode)中,所有的 assert 語句都會被忽略,不會執行

因此,不應該依賴 assert 來進行生產環境中的程式邏輯控制

B. 提升程式的可靠性

通過在程式的關鍵位置添加 assert,可以及早發現潛在的錯誤,從而提高程式的可靠性和穩定性

● 範例如下,當 assert 內部的判斷為 false 時發生中斷,並拋出 AssertionError


void main() {
    var a = 100;
    assert(a == 99);
}


Dart 引用庫

在 Dart 中,透過 import 關鍵字和指定路徑,開發者可以匯入各種類型的函式庫,包括 Dart 標準函式庫、第三方函式庫以及專案內的自訂函式庫

根據函式庫的來源和用途,Dart 使用不同的路徑前綴來明確區分這些函式庫的類型

庫分類關鍵字範例
Dart 語言(標準庫)dart:dart:io
第三方庫(通常可以到 pub.dev 找)package:package:hello/world.dart

指定庫別名:as

● 在 Dart 中,有時候你可能會遇到不同的函式庫中存在相同名稱的類別、函數或變數

為了避免命名衝突,並提高程式碼的可讀性和可維護性,Dart 提供了 as 關鍵字,用於為匯入的程式庫指定一個別名

關鍵字解釋
as庫另外命名

概念範例如下


// 舉例 (並沒有這個第三方庫) 假設內部都有 Json class
import 'package:lib1/json1.dart';
import 'package:lib2/json2.dart' as Json2;

void main() {
    // lib 1 中的 Json
    Json j = new Json();

    // lib 2 中的 Json
    Json2.Json j2 = new Json2.Json();
}

庫部分引用:show / hide

● Dart 提供了靈活的方式來引用庫中的部分內容,以便在程式碼中只引入必要的部分;這不僅可以減少不必要的依賴,還可以提高程式碼的可讀性和效能

使用關鍵字如下表

關鍵字解釋
show部分引用
hide隱藏特定部分之外都引用
deferred as'庫的懶加載' 並在合適的時候使用 loadLibrary() 方法

概念範例如下


// lib1 指使用 Json class
import 'package:lib1/json1.dart' show Json;

// lib2 除了 Json class 之後都使用
import 'package:lib2/json2.dart' hide Json;

// 改名為 T
import 'package:xxx/ui/fragment/transfer/Transfer.dart' as T;'

void main() {
    T.Transfer(); // 建構函數上就必須加上 T
    popCalendarDialog();
}

import 'package:xxx/utils/Calendar.dart' deferred as Calendar;'

void popCalendarDialog() async {
    await Calendar.loadLibrary();        // 開始加載庫
    Calendar.open();
}

更多的 Flutter/Dart 語言相關文章

了解 Flutter 如何在跨平台開發中佔據重要地位,掌握快速上手的技巧與項目建置流程,開啟你的跨平台開發之旅!

探索跨平台與 Flutter 技術的未來:從認識到 Flutter 專案建置 | 3 種跨平台

Dart 語言基礎

探討 Dart 語言:宣告、數據類型、操作符 | 從基礎到應用指南

快速掌握 Dart 語言的核心概念,包括變數宣告、數據類型及操作符,為 Flutter 開發奠定扎實基礎。

Dart 函數與方法、異常處理、引用庫 | Java 比較

深入了解 Dart 的函數與異常處理特性,並與 Java 的處理方式進行比較,幫助你跨語言切換更加順暢。

深入解析 Dart 語言:命名慣例、類特性、建構函數與抽象特性

學習 Dart 類的設計邏輯及命名慣例,深入探索抽象類與 Mixin 的強大應用場景。

深入探索 Dart 的併發與異步處理:從 Isolate 到 Event Loop 的全面指南 | Future、Stream

徹底搞懂 Dart 的併發與異步處理,掌握 Isolate 與 Event Loop 的運行機制,助你提升應用效能!

深入 Flutter 框架

深入解析 Flutter Navigator:常見錯誤、解決方法與路由跳轉技巧、動畫

從常見問題到自定義解決方案,學會如何利用 Navigator 實現路由跳轉與流暢動畫效果。

深入理解 Flutter 中的數據共享:從普遍方案到 InheritedWidget | 3 種方案

探討數據共享的最佳實踐,了解 InheritedWidget 等三種主要方案,幫助你優化應用結構。

深入解析 Flutter 三顆樹:Widget、Element 與 RenderObject 完整指南

拆解 Flutter 的內部結構,全面了解 Widget、Element 和 RenderObject 之間的關係,提升你的 Flutter 開發技能!

Leave a Comment

Comments

No comments yet. Why don’t you start the discussion?

發表迴響