iT邦幫忙

2024 iThome 鐵人賽

DAY 5
0
Mobile Development

從零開始以Flutter打造跨平台聊天APP系列 第 5

Day-5 Dart 簡介(4):Class, Abstract Class

  • 分享至 

  • xImage
  •  

Generated from Stable Diffusion 3 Medium

Dart 是物件導向的語言,擁有很多物件導向語言的特性。其中,類別 class 可以將變數及函式進行封裝。所謂的物件,就是根據 class 所製造出來的 instance (實例)。由於物件導向本身有一定難度,本次教學只會介紹 Dart 中物件導向的特性和寫法,無法詳盡介紹整個物件導向概念。

如果讀者本身沒有寫過 class 但有寫過 struct。可以想像 class 就是功能更多的 struct,除了可以封裝變數之外,也可以綁定函式。另外,在 C 語言中,我們常常會以 malloc() 去產生 struct ,而在其他語言,比如 Java, Javascript 則會使用 new 其實道理是類似的。早期的 Dart 也會使用 new 這個關鍵字去實例化一個物件,但後來發現其實有點多餘,所以新版的 Dart ,可以省略 new 不寫。

在設計 class 時,我們通常會將 class 名稱第一個字母以大寫表示,這個「潛規則」跟 Java 是一樣的。另外,我們未來會提供 public, private 的概念,在 dart 中,若 class 或 class 中的 member 以下劃線 _ 開頭,則代表是 private。這種以下劃線代表 private 的潛規則,出現在 Javascript 中,因為早期的 Javascript 無法像 Java 一樣指定 public 或 private。有趣的是,在 Go 語言中,沒有完整的物件導向,當變數的第一個字母為大寫時,代表 public;為小寫時代表 private。每個語言都有自己一套規則!

class Point {
  // 略
}
var p1 = new Point(); // 實例化一個 Point 物件
var p2 = Point(); // new 關鍵字可省略

範例程式碼:https://github.com/ksw2000/ironman-2024

官方參考文件:

Constructor

在實例化一個物件時,比如實例化 Point 時,我們可以呼叫利用 Point() 來實例化,這個作法可以產生一個空的 Point。在 Dart 中,提供程式設計師設計不同的建構元。比如以下的範例,可以透過 Point.Zero() 建構一個原點座標。

class Point {
  double? x;
  double? y;
  Point(double x, double y) {
    this.x = x;
    this.y = y;
  }
  Point.origin() {
    this.x = 0;
    this.y = 0;
  }
}

void main(){
  var p = Point(3, 4);
  print("${p.x} ${p.y}"); // 3.0 4.0
  var q = Point.origin();
  print("${q.x} ${q.y}"); // 0.0 0.0
}

另一個常見的例子是 ListList 有不同的建構元,比如 List.empty() 可以建構一個空的 List()List.filled(n, e) 可以一建立 n 個元素 e 的 List。

List<int> list1 = List.filled(5, 1);
List<int> list2 = List.empty();
print(list1); //[1, 1, 1, 1, 1]
print(list2); //[]

建構函式通常會將值直接賦予物件內的成員,由於這個操作太常出現,於是 Dart 提供簡化的寫法。

class Point {
  double? x;
  double? y;
  Point(double x, double y) {
    this.x = x;
    this.y = y;
  }
  Point.origin() {
    this.x = 0;
    this.y = 0;
  }
}

// Point2 的寫法與 Point1 相同
class Point2 {
  double x;
  double y;
  Point2(this.x, this.y);
  Point2.origin()
      : this.x = 0,
        this.y = 0;
}

Getter & Setter

物件導向的語言中,常常會需要對一個值做存取,也許這個值並不真的存在,此時我們就必需自己手動設定 getter 和 setter,如以下範例:

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

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

  double getRight() {
    return left + width;
  }

  void setRight(double value) {
    left = value - width;
  }

  double getBottom() {
    return top + height;
  }

  void setBottom(double value) {
    top = value - height;
  }
}

void main(){
  var r = Rectangle(0, 0, 30, 40);
  r.setBottom(50);
  r.setRight(40);
  print("${r.top}, ${r.left}"); // 10.0, 10.0
}

上面的範例示範了在不使用 getter 與 setter 的情況下的實現方式。我們可以利用 Dart 提供的 getter 和 setter 簡化程式碼。Javascript, Kotlin, Swift 都有類似的語法糖。

class Rectangle2 {
  double left, top, width, height;

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

  // Define two calculated properties: right and bottom.
  double get right => left + width;
  set right(double value) => left = value - width;
  double get bottom => top + height;
  set bottom(double value) => top = value - height;
}

void main(){
  var s = Rectangle2(0, 0, 30, 40);
  s.bottom = 50;
  s.right = 40;
  print("${s.top}, ${s.left}"); // 10.0, 10.0
}

Abstract class

有時,我們會希望使用者自行設計一個包含某些方法的 class。當使用建造完這種 class 後,才方便被後續的程式碼使用。這時,我們可以設計一個模版,稱為抽象類別 abstract class,這個抽象類別只是告訴使用者要怎麼去設計,而且抽象類別不可以直接被實例化。使用者必需設計一個 class 去 extends 這個模版,並且按照需求去 @override 裡面的方法。假如使用者設計一個 class A ,而 A 繼承 (extends) 了某一個抽像類別 B 後,此時,class A 的實例的型別,就可以同時是 A 也可以是 B。「繼承」這個概念以型別來看就類似俄羅斯娃娃,A 繼承 B,B 繼承 C,那麼 A 的實例型別上就可以是 A 或 B 或 C。

abstract class Animal {
  void bark();
}

class Cat extends Animal {
  @override
  void bark() {
    print("喵");
  }
}

class Dog extends Animal {
  @override
  void bark() {
    print("汪");
  }
}

void barking(Animal m) {
  m.bark();
}

void main(){
  var cat = Cat(); // cat 的型別是 Cat,同時也符合 Animal
  var dog = Dog(); // dog 的型別是 Dog,同時也符合 Animal
  barking(cat); // barking 吃的型別是 Animal,Cat 符合 Animal
  barking(dog); // barking 吃的型別是 Animal,Dog 符合 Animal
}

以上的情境,在 Java 中可以利用 interfaceabstract class 實現。雖然這兩個用法要解決的事情大致相同,但使用上仍有一些差異,比如一個 class 在實作時,可以實作不同的 interface,但卻只能繼承一個 abstract class。而在 Dart 中,我們在此章節會先介紹最簡單的 abstract class,後續會再提到更進階的操作。


後記:今天出去玩,時間很趕,差點沒寫完。然後我有點擔心教不完了


上一篇
Day-4 Dart 簡介(3):Function
下一篇
Day-6 Dart 簡介(5):Extends, Mixin, Implement, Interface
系列文
從零開始以Flutter打造跨平台聊天APP25
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言