Java 開發人員專用的 Dart 簡介

透過集合功能整理內容 你可以依據偏好儲存及分類內容。

1. 簡介

Dart 是 Flutter 的程式設計語言。Google 的 UI 工具包可讓您從單一程式碼集內,建構出原生且以原生方式編譯的行動版、網頁和電腦版應用程式。

本程式碼研究室會為您介紹 Dart,並將重點放在 Java 開發人員所期望的功能。您在 1 分鐘內可以寫入 Dart 函數,在 5 分鐘內進行腳本,在 10 分鐘內進行應用程序!

課程內容

  • 如何建立建構函式
  • 指定參數的各種方式
  • 建立 getter 和 setter 的時機和方式
  • Dart 如何處理隱私權
  • 如何建立工廠
  • 功能計劃在 Dart 中的運作方式
  • Dart 其他核心概念

軟硬體需求

只要完成瀏覽器,就能完成這個程式碼研究室!

您將在 DartPad 中撰寫及執行所有範例,而這個互動性瀏覽器是以瀏覽器為基礎,可讓您利用 Dart 語言功能和核心程式庫進行遊戲。如有需要,您可以改用 IDE,例如 WebStorm、Dart 外掛程式的 IntelliJ,或搭配 Dart Code 擴充功能使用 Visual Studio Code。

您想從這個程式碼研究室中學習什麼?

我對主題很熟悉,而且希望對大家有良好的瞭解。 我知道這個主題,但想要複習一下。 我正在尋找要用於專案的範例程式碼。我想尋找特定資訊的說明。

2. 建立簡單的 Dart 類別

首先,請建立簡單的 Dart 類別,其功能與 Java 教學課程中的 Bicycle 類別相同。Bicycle 類別包含一些包含 getter 和 setter 的私人執行個體變數。main() 方法可將 Bicycle 執行個體化並列印至主控台。

99c813a1913dcc42.png c97a12197358d545.png

啟動 DartPad

本程式碼研究室為每組練習提供新的 DartPad 實例。下方連結會開啟新的執行個體,其中包含預設的「Hello」範例。您可以在程式碼研究室中繼續使用相同的 DartPad,但如果您按一下 [重設],DartPad 會回到預設範例,讓您遺失工作內容。

b2f84ff91b0e1396.png 開啟 DartPad

定義單車類別

b2f84ff91b0e1396.pngmain() 函式上方,新增包含三個執行個體變數的 Bicycle 類別。同時移除 main() 中的內容,如以下程式碼片段所示:

class Bicycle {
  int cadence;
  int speed;
  int gear;
}

void main() {
}

cf1e10b838bf60ee.png 觀察項目

  • 在此範例中,Dart 分析器會產生錯誤,通知您這些變數必須初始化,因為變數不能為空值。我們將在下一節加以說明。
  • Dart 的主要方法為 main()。如果您需要指令列引數的存取權,可以新增下列引數:main(List<String> args)
  • main() 方法位於頂層。在 Dart 中,您可以在類別外定義程式碼。變數、函式、getter 和 setter 都可以在類別外使用。
  • 原始 Java 範例會使用 private 標記宣告私人執行個體變數 (Dart 不使用)。稍後,請參閱「新增唯讀變數」,進一步瞭解隱私權相關資訊。
  • 由於所有 ID 皆預設為公開,因此 main()Bicycle 均未宣告為 public。Dart 沒有「public」、「private」或「protected」的關鍵字。
  • Dart 會根據慣例使用雙字元縮排,而非四個字元。您無須擔心 Dart's 的空白字元慣例,因為我們特別設計了一款稱為「art 格式」的實用工具。如同《Dart 程式碼慣例》(Effective Dart) 所說的:「The Dart 官方空白處理規則是 Dart 格式產生的內容。」

定義單車建構函式

b2f84ff91b0e1396.png 將下列建構函式新增至 Bicycle 類別:

Bicycle(this.cadence, this.speed, this.gear);

cf1e10b838bf60ee.png 觀察項目

  • 這個建構函式沒有內文,在 Dart 中有效。
  • 如果您在無人建構函式的結尾誤刪了分號 (;),DartPad 會顯示下列錯誤訊息:「&functiont provided by 功能」。
  • 在建構函式中使用 this 是方便指派值給執行個體變數的便捷捷徑。
  • 上述程式碼等同於以下內容,其中使用初始化清單:
Bicycle(int cadence, int speed, int gear)
      : this.cadence = cadence,
        this.speed = speed,
        this.gear = gear;

設定程式碼格式

只要按一下 DartPad 使用者介面頂端的 [格式],即可隨時重新設定 Dart 程式碼的格式。將程式碼貼到 DartPad 且關閉原因時,格式特別有用。

b2f84ff91b0e1396.png 按一下 [格式]

將列印執行個體執行個體化並列印

b2f84ff91b0e1396.png 將下列程式碼加進 main() 函式:

void main() {
  var bike = new Bicycle(2, 0, 1);
  print(bike);
}

b2f84ff91b0e1396.png 移除選用的 new 關鍵字:

var bike = Bicycle(2, 0, 1);

cf1e10b838bf60ee.png 觀察

  • 在 Dart 2 中,new 關鍵字是選用項目。
  • 如果您知道變數的值不會改變,可以使用 final 而非 var
  • print() 函式可接受任何物件 (而不只是字串)。並使用物件的 toString() 方法將其轉換為 String

執行範例

b2f84ff91b0e1396.png 按一下 DartPad 視窗頂端的 [Run] (執行) 即可執行範例。如果「執行」未啟用,請參閱本頁稍後的「問題」部分。

您應該會看到下列輸出:

Instance of 'Bicycle'

cf1e10b838bf60ee.png 觀察

  • 不應出現任何錯誤或警告,表示類型推論功能運作正常,且分析器推論開頭為 var bike = 的陳述式定義了自行車執行個體。

改善輸出內容

而“Bicycle'”的實例是輸出的,但是正的,但它不是很信息。所有 Dart 類別都有 toString() 方法,您可以加以覆寫,以提供更實用的輸出內容。

b2f84ff91b0e1396.pngBicycle 類別的任何位置新增下列 toString() 方法:

@override
String toString() => 'Bicycle: $speed mph';

cf1e10b838bf60ee.png 觀察項目

  • @override 註解會告訴分析器您打算覆寫某個成員。如果您未正確執行覆寫,分析工具就會引發錯誤。
  • Dart 支援指定字串時可以使用單引號或雙引號。
  • 使用字串內插值將運算式的值放在字串常值中:${expression}。如果運算式為 ID,則可略過括號:$variableName
  • 使用脂肪箭頭 (=>) 標記法減少單行函式或方法。

執行範例

b2f84ff91b0e1396.png 按一下 [執行]

您應該會看到下列輸出:

Bicycle: 0 mph

遇到問題嗎?請檢查您的驗證碼

新增唯讀變數

原始 Java 範例將 speed 定義為唯讀變數,它會將其宣告為私人,且只會提供 getter。接下來,您在 Dart 中也能提供相同的功能。

b2f84ff91b0e1396.png 在 DartPad 中開啟 bicycle.dart (或繼續使用您的複本)。

如要將 Dart ID 設為非公開資料庫,請在名稱開頭加上底線 (_)。只要變更 speed 的名稱,然後新增 getter,即可將 speed 轉換為唯讀狀態。

將速度設為私人的唯讀變數

b2f84ff91b0e1396.pngBicycle 建構函式中,移除 speed 參數:

Bicycle(this.cadence, this.gear);

b2f84ff91b0e1396.pngmain() 中,移除呼叫 Bicycle 建構函式的第二個 (speed) 參數:

var bike = Bicycle(2, 1);

b2f84ff91b0e1396.png 將剩餘的 speed 次變更為 _speed。(兩個地點)

b2f84ff91b0e1396.png_speed 初始化為 0:

int _speed = 0;

b2f84ff91b0e1396.png 將下列 getter 新增至 Bicycle 類別:

int get speed => _speed;

cf1e10b838bf60ee.png 觀察項目

  • 每個變數 (即使是數字) 都必須初始化,或者在其類型宣告中加入 ? 宣告為空值。
  • Dart 編譯器會針對加上底線的 ID 強制實行資料庫隱私權。圖書館隱私權通常代表 ID 僅供檔案內的定義 (而不只是類別) 顯示。
  • 根據預設,Dart 會為所有公開的執行個體變數提供隱含的 getter 和 setter。除非您想要強制執行唯讀或僅限寫入的變數、計算或驗證值,或是更新其他位置的值,否則不需要定義自己的 getter 或 setter。
  • 原始 Java 範例提供 cadencegear 的 getter 和 setter。Dart 範例不需要明確取得 getter 和 setter,因此只會使用執行個體變數。
  • 您可以先使用一個簡單的欄位 (例如 bike.cadence),然後再重新計算該欄位,以使用 getter 和 setter。API 維持不變。換句話說,在某個領域中,從一個欄位變成發射器和設定器,在 Dart 中並沒有改變。

以唯讀執行個體變數的形式完成導入速度

b2f84ff91b0e1396.png 將下列方法新增至 Bicycle 類別:

void applyBrake(int decrement) {
  _speed -= decrement;
}

void speedUp(int increment) {
  _speed += increment;
}

Dart 的最終版範例與原始 Java 類似,但以 23 行 (而非 40 行) 更為精簡:

class Bicycle {
  int cadence;
  int _speed = 0;
  int get speed => _speed;
  int gear;

  Bicycle(this.cadence, this.gear);

  void applyBrake(int decrement) {
    _speed -= decrement;
  }

  void speedUp(int increment) {
    _speed += increment;
  }

  @override
  String toString() => 'Bicycle: $_speed mph';
}

void main() {
  var bike = Bicycle(2, 1);
  print(bike);
}

遇到問題嗎?請檢查您的驗證碼

3. 使用選用參數 (而非超載)

下個練習會定義 Rectangle 類別,這是 Java 教學課程中的另一個範例。

Java 程式碼會顯示超載建構函式,這是一種 Java 常見的實作方法,其建構函式的名稱相同,但在參數的數量或類型上不同。Dart 不支援超載建構函式及處理各種情況,正如本節所述。

b2f84ff91b0e1396.png 開啟 DartPad 中的矩形範例

新增矩形建構函式

b2f84ff91b0e1396.png 新增一個空白的建構函式,取代 Java 範例中的所有四個建構函式:

Rectangle({this.origin = const Point(0, 0), this.width = 0, this.height = 0});

此建構函式使用自選的命名參數

cf1e10b838bf60ee.png 觀察項目

  • this.originthis.widththis.height 使用捷徑技巧在建構函式宣告的執行個體上指派變數。
  • this.originthis.widththis.height 是選用的已命名參數。已命名參數會以大括號 ({}) 括住。
  • this.origin = const Point(0, 0) 語法會為 origin 執行個體變數指定預設值 Point(0,0)。指定的預設值必須為編譯時間常數。這個建構函式可以針對三個執行個體變數提供預設值。

改善輸出內容

b2f84ff91b0e1396.png 將下列 toString() 函式新增至 Rectangle 類別:

@override
String toString() =>
      'Origin: (${origin.x}, ${origin.y}), width: $width, height: $height';

使用建構函式

b2f84ff91b0e1396.pngmain() 替換成下列程式碼,以確認您只使用所需參數執行個體化 Rectangle

main() {
  print(Rectangle(origin: const Point(10, 20), width: 100, height: 200));
  print(Rectangle(origin: const Point(10, 10)));
  print(Rectangle(width: 200));
  print(Rectangle());
}

cf1e10b838bf60ee.png 觀察

  • Rectangle 的 Dart 建構函式是一行程式碼,與 Java 版本中同等建構函式的 16 行程式碼不同。

執行範例

您應該會看到下列輸出:

Origin: (10, 20), width: 100, height: 200
Origin: (10, 10), width: 0, height: 0
Origin: (0, 0), width: 200, height: 0
Origin: (0, 0), width: 0, height: 0

遇到問題嗎?請檢查您的驗證碼

4. 建立工廠

因素是 Java 中常用的設計模式,與直接物件執行個體化有幾項優勢,例如隱藏執行個體化的細節、提供出廠的退貨類型子類型,以及選擇性傳回現有物件 (而非新物件)。

這個步驟將說明兩種製作圖形工廠的方法:

  • 選項 1:建立頂層函式。
  • 選項 2:建立工廠建構函式。

在本練習中,您將使用圖形範例,建立形狀並列印計算結果區域:

import 'dart:math';

abstract class Shape {
  num get area;
}

class Circle implements Shape {
  final num radius;
  Circle(this.radius);
  num get area => pi * pow(radius, 2);
}

class Square implements Shape {
  final num side;
  Square(this.side);
  num get area => pow(side, 2);
}

main() {
  final circle = Circle(2);
  final square = Square(2);
  print(circle.area);
  print(square.area);
}

b2f84ff91b0e1396.png 開啟 DartPad 中的 Shapes 範例

在主控台區域中,您應該會看到圓形和正方形的運算區域:

12.566370614359172
4

cf1e10b838bf60ee.png 觀察項目

  • Dart 支援抽象類別。
  • 您可以在一個檔案中定義多個類別。
  • dart:math 是 Dart 的核心程式庫之一。其他核心程式庫包括 dart:coredart:asyncdart:convertdart:collection
  • 按照慣例,Dart 程式庫常數是 lowerCamelCase (例如,使用 pi,而不是 PI))。如果您想知道原因為何,請參閱樣式指南 PREFER,使用常數 CamelCase 以取得常數名稱
  • 下列程式碼顯示兩個用來計算值的 getter:num get area => pi * pow(radius, 2); // Circle num get area => pow(side, 2); // Square

選項 1:建立頂層函式

b2f84ff91b0e1396.png 將廠牌範例設為頂層函式 (可在任何類別之外加上以下函式):

Shape shapeFactory(String type) {
  if (type == 'circle') return Circle(2);
  if (type == 'square') return Square(2);
  throw 'Can\'t create $type.';
}

b2f84ff91b0e1396.png 請替換 main() 方法中的前兩行,叫用工廠函式:

  final circle = shapeFactory('circle');
  final square = shapeFactory('square');

執行範例

輸出結果應與之前相同。

cf1e10b838bf60ee.png 觀察項目

  • 如果使用 'circle''square' 以外的任何字串呼叫此函式,則系統會擲回例外狀況。
  • Dart SDK 定義許多常見例外類別,或者您可以導入 Exception 類別來建立更明確的例外,或者也可在這個範例中擷取一個用來描述問題的字串。
  • 發生例外狀況時,DartPad 會回報 Uncaught。如要查看更實用的資訊,請將程式碼包裝在 try-catch 陳述式中並列印例外狀況。建議您參考這個 DartPad 範例
  • 如要在字串中使用單引號,可以使用斜線 ('Can\'t create $type.') 逸出內嵌引號,或使用雙引號 ("Can't create $type.") 指定字串。

遇到問題嗎?請檢查您的驗證碼

選項 2:建立工廠建構函式

使用 Dart's factory 關鍵字來建立工廠建構函式。

b2f84ff91b0e1396.png 將工廠建構函式新增至抽象 Shape 類別:

abstract class Shape {
  factory Shape(String type) {
    if (type == 'circle') return Circle(2);
    if (type == 'square') return Square(2);
    throw 'Can\'t create $type.';
  }
  num get area;
}

b2f84ff91b0e1396.pngmain() 的前兩行替換為下列程式碼,將形狀執行個體化:

  final circle = Shape('circle');
  final square = Shape('square');

b2f84ff91b0e1396.png 刪除您先前新增的 shapeFactory() 函式。

cf1e10b838bf60ee.png 觀察

  • 工廠建構函式中的程式碼與 shapeFactory() 函式中使用的程式碼相同。

遇到問題嗎?請檢查您的驗證碼

5. 導入介面

Dart 語言不包含 interface 關鍵字,因為所有類別都會定義介面

b2f84ff91b0e1396.png 在 DartPad 中開啟形狀範例 (或繼續使用您的複本)。

b2f84ff91b0e1396.png新增一個導入 Circle 介面的 CircleMock 類別:

class CircleMock implements Circle {}

b2f84ff91b0e1396.png 您應該會看到「缺少具體的實作」錯誤,因為 CircleMock 不會沿用 Circle實作,而該設定只會使用其介面。定義 arearadius 執行個體變數來修正這個錯誤:

class CircleMock implements Circle {
  num area = 0;
  num radius = 0;
}

cf1e10b838bf60ee.png 觀察

  • 即使 CircleMock 類別未定義任何行為,但有效的有效的 Dart 也會讓分析器引發任何錯誤。
  • CircleMockarea 執行個體變數會導入 Circlearea getter。

遇到問題嗎?請檢查您的驗證碼

6. 使用 Dart 進行函式程式設計

在功能性程式設計中,您可以執行下列操作:

  • 將函式做為引數傳遞。
  • 為函式指派函式。
  • 解構函式,將多個引數編入一系列函式,每個函式接收一個引數 (也稱為「收錄」)。
  • 建立一個不可用作常數值的不命名函數(也為 lambda 表述; lambda 表情在 JDK 8 版本 中添加到 Java)。

Dart 支援上述所有功能。在 Dart 中,即使函式為物件,其類型也為 Function。這表示函式可以指派給變數,或做為引數傳送至其他函式。您也可以呼叫 Dart 類別的執行個體,如同函式 (如這個範例所示)。

以下範例使用強制性 (非函式式) 程式碼:

String scream(int length) => "A${'a' * length}h!";

main() {
  final values = [1, 2, 3, 5, 10, 50];
  for (var length in values) {
    print(scream(length));
  }
}

b2f84ff91b0e1396.png 開啟 DartPad 中的 Scream 範例

輸出內容應如下所示:

Aah!
Aaah!
Aaaah!
Aaaaaah!
Aaaaaaaaaaah!
Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaah!

cf1e10b838bf60ee.png 觀察

  • 使用字串內插時,${'a' * length} 字串會評估為「'a'」字元重複 length 次。

將必要程式碼轉換成可正常運作的

b2f84ff91b0e1396.png 移除 main() 中必要的 for() {...} 迴圈,並將其替換為使用方法鏈結的單行程式碼:

  values.map(scream).forEach(print);

執行範例

功能方法會印出和

遇到問題嗎?請檢查您的驗證碼

使用更多疊代功能

核心 ListIterable 類別支援 fold()where()join()skip() 等等。Dart 也內建支援地圖和地圖集。

b2f84ff91b0e1396.pngmain() 中的 values.map() 行替換為下列內容:

  values.skip(1).take(3).map(scream).forEach(print);

執行範例

輸出內容應如下所示:

Aaah!
Aaaah!
Aaaaaah!

cf1e10b838bf60ee.png 觀察項目

  • skip(1)略過 values 清單常值中的第一個值 (1)。
  • take(3)values 清單常值中取得接下來的 3 個值 (2、3 和 5)。
  • 其餘的值將被略過。

遇到問題嗎?請檢查您的驗證碼

7. 恭喜!

完成這個程式碼研究室後,您就能深入瞭解 Java 和 Dart 之間的差異。Dart 不僅容易學習,還提供了核心程式庫和多種豐富的套裝套件,能協助您提高生產力。Dart 可以大規模地應用在各種應用程式上。數百位 Google 工程師使用 Dart 撰寫重要應用程式,從中獲取大部分的 Google 收益。

後續步驟

20 分鐘的程式碼研究室還不足以顯示 Java 和 Dart 的所有差異。例如,這個程式碼研究室未涵蓋:

如果您想查看 Dart 技術的實際運作情形,不妨試試 Flutter 程式碼研究室

瞭解詳情

如要進一步瞭解 Dart,請參閱下列文章、資源和網站。

Articles

資源

網站