iT邦幫忙

0

Dart 語言入門 5: 類別(Class)

Dart是一個物件導向語言,同時支持混入(mixin)的繼承機制。每個物件都是一個類別的實體,所有的類別都繼承於object。基於Mixin的繼承意味著每個類別(Object除外)都只有一個父類別,一個類別可以在其他多個類別繼承中重複使用。使用new關鍵字和建構式來建立新的物件。建構式的名字可以為ClassName或者ClassName.identifier。

宣告類別

宣告一個類別,使用關鍵字class開頭,後跟類別名稱; 接著使用一對大括號包圍程式碼。如下所示

語法

class class_name {  
   <fields> 
   <getters/setters> 
   <constructors> 
   <functions> 
}

類別可包括以下內容:
欄位(fields) - 欄位是類別中宣告的變數。
getters和setters - 允許程式初始化和檢索類別欄位的值,預設的getter/setter與每個類別相關聯。但是可以通過顯式定義setter/getter來覆蓋預設值。
建構式(constructors) - 為類別的物件分配記憶體。
函式(functions) - 函式表示物件可以採取的操作,也稱為方法。
以上稱為類別的資料成員。
做一個範例如下,宣告一個類別Car。該類別有一個brand的欄位;一個簡單的方法 disp(),列印出廠牌這個欄位的值。

class Car {
  // field
  String brand = 'Tesla';

  // function
  void disp() {
    print(brand);
  }
}

建立類別的實體(Creating Instance of the class)

語法
目前建立類別的實體,不用加上new, 這樣看起來更簡潔。直接讓變數等於建構式即可。

var object_name = class_name([ arguments ]);

把上面的Car類別,建立出實體

void main() {
  var car1 = Car();       //建立 Car 的實體 物件:car1
  print(car1.brand);      //Tesla
}

class Car {
  // field
  String brand = 'Tesla';

  // function
  void disp() {
    print(brand);
  }
}

建構式

建構式是類別的特殊函式,負責初始化類別。建構式是一個與類別名稱相同的函式,它是一個函式,因此可以傳入參數。但是與函式不同的地方,在於建構式不能具有返回型別。如果未宣告建構式,則會預設無參數的建構式。
語法

class_name(parameter_list) { 
   //constructor body 
}

用上面的Car類別來修改,加上含有一個參數的建構式。這裡有用到 this 關鍵字, 代表物件本身。

void main() {
  var car1 = Car('Benz');
  var car2 = Car('BMW');
  print(car1.brand);
  car2.disp();
}

class Car {
  // field
  String brand = 'Tesla';
  //constructors
  Car(String b) {
    this.brand = b;
  }
  // function
  void disp() {
    print('The brand of the car is $brand');
  }
}

https://ithelp.ithome.com.tw/upload/images/20200902/20121852YnJIuqOZmD.png

命名建構式

由於Dart不支援同一名稱但是不同參數的多形建構式,以 Car 類別為例子,只能有一個名稱叫做Car的建構式,
若是需要建立其他的建構式,需要命名為其他名稱來建立,例如 Car.fromBC(brand, color)來建立。

void main() {
  var car1 = Car('Benz');
  var car2 = Car.fromBC('BMW', 'black');
  car1.color = 'red';
  car1.disp();
  car2.disp();
}

class Car {
  // field
  String brand;
  String color;
  //constructors
  Car(String brand) {
    this.brand = brand;
  }
  //這裡採用命名建構式,並使用簡寫方式建立
  Car.fromBC(this.brand, this.color);
  // function
  void disp() {
    print('The brand of the car is $brand and color is $color.');
  }
}

https://ithelp.ithome.com.tw/upload/images/20200902/20121852lntan7c80U.png

Getters和Setter

Getters(存取器)和Setter(更改器)允許程式分別初始化和檢索類欄位的值。
Getters:使用get關鍵字定義。沒有參數,會回傳值。
Setter:使用set關鍵字定義。只有一個參數,且不回傳值。
語法

//getter
return_type  get identifier 
{ 
}

//setter
set identifier 
{ 
}
void main() {
  var car1 = Car('Benz', 'red');
  var car2 = Car('BMW', 'blue');
  car1.disp();
  car2.disp();
  car1.setColor = 'black';
  print('The Color of this car reset to ${car1.getColor}');
}

class Car {
  // field
  String brand;
  String color;

  //constructors
  Car(this.brand, this.color);

  //Setter
  set setColor(String c) {
    color = c;
  }

  set setBrand(String b) {
    brand = b;
  }

  //getter
  String get getColor => color;
  String get getBrand => brand;
  // function
  void disp() {
    print('The brand of the car is $brand and color is $color.');
  }
}

https://ithelp.ithome.com.tw/upload/images/20200902/201218528G5vBoLA2t.png

靜態(static)用法

利用static 可以實現,在相同的類別之間的物件,可以共享欄位值與函式。要注意的是,若修改了類別上的static 的值,則所有該類別產生的物件都會受到影響,使用時需特別小心。

void main() {
  var person1 = Person('Dragon', 'Chen');
  var person2 = Person('Jefferson', 'Lin');
  print(person1.showFullName);
  print(person2.showFullName);
  Person.sayHi(person1);
  Person.sayHi(person2);
  Person.label = 'Your name is';
  print(person1.showFullName);
  print(person2.showFullName);
  Person.sayHi(person1);
  Person.sayHi(person2);
}

class Person {
  // field
  String firstName;
  String lastName;
  static String label = "Person's name is ";
  //constructors
  Person(this.firstName, this.lastName);

  //Setter
  set fullName(String f) {
    var temp = f.split(' ') ;
    firstName =  temp.first;
    lastName = temp.last;
  }

  set setFirstName(String f) {
    firstName = f;
  }
  set setLastName(String l){
    lastName = l;
  }
  //getter
  String get getFirstName => firstName;
  String get getLastName => lastName;
  String get showFullName => '$label $firstName $lastName';
  // static function
  static void sayHi(Person p) {
    print('Hi $label ${p.firstName} ${p.lastName}');
  }
}

https://ithelp.ithome.com.tw/upload/images/20200903/20121852fk6h0Dq0Rf.png

繼承

extends: 關鍵字來繼承類別。要注意的是,除了建構式之外,全部的成員都會繼承。不支援多重繼承。
super: 可呼叫父層變數,屬性或函式等等。。
@override: 重載(reload),也就是重新定義父層的函式,但是參數的數量和型別必須匹配。
語法

class child_class_name extends parent_class_name
void main() {  
   var circle1 = Circle(); 
     circle1.area();
     circle1.color = 'red';
     print('my color is ${circle1.showColor}'); 
}  
class Shape { 
  String color;
   void area() { 
      print("show area"); 
   } 
   String get showColor => color; 
}  
class Circle extends Shape {}

https://ithelp.ithome.com.tw/upload/images/20200903/20121852yYjWSsvwVe.png

void main() {  
    var person1 = Person('Dragon','Chen');
    var person2 = Doctor('Jefferson','Lin','Badboy');
    print(person1);
    print(person2);
}  
class Person { 
  String firstName;
  String lastName;
  String get fullName => '$firstName $lastName';
  Person(this.firstName, this.lastName);
}  
class Doctor extends Person{
  String nickName;
  Doctor(String f, String l, this.nickName) :super(f,l);
  @override String toString() => 'Hi Doctor $fullName, also known as $nickName';
}

https://ithelp.ithome.com.tw/upload/images/20200903/20121852tm3HhzmASZ.png

抽象類別(Abstract class)

使用abstract關鍵字定義一個抽象類別,一個不能被實體化的類別,抽象類別通常用來定義介面(interface)。

void main() {
  var doctor1 = Doctor('Dragon', 'Chen', 'DC');
  print(doctor1.fullName);
}

abstract class Person {
  String firstName;
  String lastName;
  String get fullName;
  Person(this.firstName, this.lastName);
}

class Doctor extends Person {
  String nickName;
  Doctor(String f, String l, this.nickName) : super(f, l);
  @override
  String get fullName =>
      'Hi Doctor $firstName $lastName, also known as $nickName';
}

https://ithelp.ithome.com.tw/upload/images/20200903/201218521ATvLpX6lH.png
例子中可以看出,實體化Doctor類別,而不是實體化Person。而Person類別中的 fullName為抽象的getter且未實作,因此在子類別 Doctor中需實作 fullName。

介面(Interface)

Dart語言中,並未定義interface這個關鍵字,而是類別本身就是介面,也就是說類別除了可以被繼承也可以被實作(implements)。

void main() {
  var doctor1 = Doctor('Dragon', 'Chen', 'DC');
  print(doctor1.fullName);
}

abstract class Person {
  String firstName;
  String lastName;
  String get fullName;
  Person(this.firstName, this.lastName);
}

class Doctor implements Person {
  String nickName;
  @override String firstName;
  @override String lastName;
  Doctor(this.firstName, this.lastName, this.nickName);
  @override
  String get fullName =>
      'Hi Doctor $firstName $lastName, also known as $nickName';
}

https://ithelp.ithome.com.tw/upload/images/20200903/20121852pzh1Jkcp0v.png
上面的例子顯示,實作介面,就像是Person類別先設定好該有的成員,然後Doctor類別將內容實作出來。

實現多個介面

實現多個介面,介面名稱之間用逗號分隔。語法如下:

class identifier implements interface-1,interface_2,interface_4…….
void main() { 
   var c = Calculator(); 
   print('The gross total : ${c.retTot()}'); 
   print('Discount :${c.retDis()}'); 
}  
class CalculateTotal { 
   // ignore: missing_return
   int retTot() {} 
}  
class CalculateDiscount { 
   // ignore: missing_return
   int retDis() {} 
}
class Calculator  implements CalculateTotal,CalculateDiscount { 
   @override
  int retTot() { 
      return 1000; 
   } 
   @override
  int retDis() { 
      return 50; 
   } 
}

https://ithelp.ithome.com.tw/upload/images/20200903/20121852pgBAPNOaYx.png

混入(Mixin)

因為只能單一繼承,若是想要增加別的類別的函式功能,類似多重繼承,就是使用混入(mixin)。
可以使用關鍵字mixin來取代class來宣告一個混入的類別,該類別就是一個讓別的類別混入的類別。
混入類別無法被繼承,且沒有建構式。若要限制使用混入的類別,可用on關鍵字指定。
使用 with 來添加混入的類別。

mixin ProgrammerSkills on Developer{
  void coding(){
    print('writing code');
  }
}
class Developer extends Person with ProgrammerSkills, ManagerSkills{
  ...
}```


尚未有邦友留言

立即登入留言