Dart基礎(四)

QiShare發表於2019-07-15

級別: ★☆☆☆☆
標籤:「Flutter 」「Dart」「Dart class」「Dart mixin」「Dart override」
作者: WYW
審校: QiShare團隊


前言
筆者在之前已經寫了3篇Dart的基礎文章了。
Dart 基礎 (一)
Dart 基礎 (二)
Dart 基礎 (三)

筆者在本文中主要會分享:類、例項變數、構造方法、命名構造方法、例項方法、靜態變數、靜態方法、set、get 方法、extendsimplementsmixinabstractoverride相關的內容。

1.類

下邊筆者先以Point 類為例,分享下關於例項變數構造方法命名構造方法例項方法靜態方法靜態變數set get方法 的內容。

Dart 是一種物件導向的程式語言,同時支援基於 mixin 的繼承機制。mixin相關的內容會在下文解釋。每個物件都是一個類的例項,所有的類都繼承於 Object。 基於 Mixin 的繼承 意味著每個類(Object 除外) 都只有一個超類,一個類的程式碼可以在其他 多個類繼承中重複使用。

使用 new 關鍵字和構造方法來建立新的物件。 構造方法名字可以為 ClassName 或者 ClassName.identifier。 在Dart2.0的時候,建立新的物件的時候,new 關鍵字是可選的。當前Dart最新版本是2.4.0,2019-06-27 Dart開發團隊釋出2.4.0版本Dart。 Dart change log

1.1 例項變數

class Point {
  
  // 例項變數
  num x;
  num y;
}
複製程式碼

1.2 構造方法:構造方法 定義一個和類名一樣的方法

 // 構造方法 定義一個和類名一樣的方法
  Point(num x, num y) {
    // this 關鍵字指當前的例項
    this.x = x;
    this.y = y;
  }
  
  // 由於把構造方法引數賦值給例項變數的場景太常見了, Dart 提供了一個語法糖來簡化這個操作
  // Point(this.x, this.y);
複製程式碼

1.3 命名構造方法

 // 命名構造方法
  Point.fromJson(Map json) {
    // 只有當名字衝突的時候才使用 this。否則的話, Dart 程式碼風格樣式推薦忽略 this。
    x = json['x'];
    y = json['y'];
  }

  Point.namedConstructor(Map json){
    x = json['x'];
    y = json['y'];
  }

複製程式碼

命名構造方法使用場景有:模型類中解析資料場景。

舉個簡單例子:如返回一個列表資料的情況,返回資料可能是是一個包著多個字典的陣列,那麼,處理相應資料的時候,需要對資料進行相應的解析。解析的過程就可能用到命名構造方法。把一個個字典當做例項,提取出來。

[
	{
		"name":"QiShare1",
		"age":"1"
	},
	{
		"name":"QiShare2",
		"age":"1"
	},
	{
		"name":"QiShare3",
		"age":"1"
	},
	{
		"name":"QiShare4",
		"age":"1"
	},
	{
		"name":"QiShare5",
		"age":"1"
	},
	{
		"name":"QiShare6",
		"age":"1"
	},
	{
		"name":"QiShare7",
		"age":"1"
	},
]
複製程式碼

1.4 例項方法

// 例項方法
  num distanceTo(Point other) {
    var dx = x - other.x;
    var dy = y - other.y;
    return sqrt(dx*dx + dy*dy);
  }
複製程式碼

1.5 靜態方法

使用static關鍵字修飾的方法為靜態方法,相當於類方法。使用類名可以直接呼叫。

// 靜態方法
  static num distanceBetween(Point a, Point b) {
    var dx = a.x - b.x;
    var dy = a.y - b.y;
    return sqrt(dx * dx + dy * dy);
  }
複製程式碼
void classDemo() {

    var jsonData = jsonDecode('{"x":2, "y":2}');
    // Create a Point using Point().
    var p1 = new Point(1, 1);
    print('p1點x座標:${p1.x}');
    print('p1點y座標:${p1.y}');

    // Create a Point using Point.fromJson().
    var p2 = new Point.fromJson(jsonData);
    print('p2點x座標:${p2.x}');
    print('p2點y座標:${p2.y}');

    num distance = p2.distanceTo(p1);
    print('p1到p2的距離: $distance');

    Map jsonData3 = {
      'x': 3,
      'y': 3,
    };

    Point p3 = Point.namedConstructor(jsonData3);
    print('p3點x座標:${p3.x}');
    print('p3點y座標:${p3.y}');
    
    num distance12 = Point.distanceBetween(p1, p2);
    print('p1和p2之間的距離 $distance12');
    
    }
    
複製程式碼

輸出內容

flutter: p1點x座標:1
flutter: p1點y座標:1
flutter: p2點x座標:2
flutter: p2點y座標:2
flutter: p1到p2的距離: 1.4142135623730951
flutter: p3點x座標:3
flutter: p3點y座標:3
flutter: p1和p2之間的距離 1.4142135623730951

複製程式碼

1.6 靜態變數

靜態變數對於類級別的狀態是非常有用的,筆者對這句話的理解是:靜態變數可以由類名直接呼叫。

class Color {
  static const red =
      const Color('red'); // A constant static variable.
  final String name;      // An instance variable.
  const Color(this.name); // A constant constructor.
}
複製程式碼

使用方式

	String colorName = Color.red.name;
    print('colorName:$colorName');
複製程式碼

輸出內容

colorName:red`
複製程式碼

1.7 set get 方法

下邊筆者舉了一個類Rectangle的left、top、width、height的Set、Get方法的例子。

class Rectangle {
  num left;
  num top;
  num width;
  num height;

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

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

複製程式碼

使用方式

Rectangle rectangel = Rectangle(0, 0, 375, 667);
    print('rectangel.left:');
    print(rectangel.left);
    print('rectangel.right:');
    print(rectangel.right);
    print('rectangel.width:');
    print(rectangel.width);
    print('rectangel.height:');
    print(rectangel.height);
    print('rectangel.right:');
    print(rectangel.right);
    print('rectangel.bottom:');
    print(rectangel.bottom);

複製程式碼

輸出結果:

flutter: rectangel.left:
flutter: 0
flutter: rectangel.right:
flutter: 375
flutter: rectangel.width:
flutter: 375
flutter: rectangel.height:
flutter: 667
flutter: rectangel.right:
flutter: 375
flutter: rectangel.bottom:
flutter: 667
複製程式碼

2. extends 與 implements

extends

關鍵字extends 用於繼承父類的例項變數及方法等。Dart 只支援單繼承。

implements

Every class implicitly defines an interface containing all the instance members of the class and of any interfaces it implements. If you want to create a class A that supports class B’s API without inheriting B’s implementation, class A should implement the B interface.

每個類都隱式地宣告瞭一個包含所有的例項變數和類已經實現的介面。
如果你想建立一個類A,沒有繼承類B,但是類A可訪問類B的API,那麼類A 應該實現類B的介面。
上邊的內容,結合著下邊的例子,筆者的理解是:Chicken隱式地宣告瞭Animal 的例項變數,和類Animal 已經實現的方法。Chicken支援在沒有繼承類Animal的情況下,可訪問類B的API。
一個類可以implements 多個類的API,所以implements算是一種變向實現多繼承的方式。


class Animal {
  String name;
  void ability() {
    print('Animal 的能力');
  }
}

class Bird extends Animal {
  void ability(){
    print('bird can fly');
  }
}

class Fish extends Animal {
  void ability(){
    print('fish can swim');
  }
}

class Dog extends Animal {
  void ability(){
    print('dog can bark');
  }
}

class Chicken implements Animal {
  String name;
  void ability() {
    print('chicken can lay eggs');
  }
}

複製程式碼

呼叫如上程式碼的方式及相應輸出結果如下:

    Dog dog = Dog();
    dog.ability();
    Fish fish = Fish();
    fish.ability();
    Bird bird = Bird();
    bird.ability();
    Chicken chicken = Chicken();
    chicken.ability();
    
    

// 輸出結果:
flutter: dog can bark
flutter: fish can swim
flutter: bird can fly
flutter: chicken can lay eggs
複製程式碼

3. mixin

Mixins 是一種在多類繼承中重用一個類程式碼的方法。筆者的理解是,mixin相當於是一個工具類,使用 with 關鍵字使用了mixin的類,就可以使用mixin中的程式碼。

Mixins are a way of reusing a class’s code in multiple class hierarchies.

To use a mixin, use the with keyword followed by one or more mixin names. The following example shows two classes that use mixins:

Mixin 是一種在多個類中重用某些程式碼的方式。 使用mixin ,需使用 with 關鍵字,with後邊跟mixin的名,with 後邊可以跟多個mixin名字,及可以同時使用多個mixin中的程式碼。下邊筆者舉了一個開發者學習基礎語言的例子。

筆者定義了一個Developer的mixin,如果是iOS 開發者需要先學習C語言基礎,如果是Android 開發者,需要先學習Java語言,如果是Flutter 開發者,需要先學習Dart 語言。

mixin Developer {
    bool isIOS = false;
    bool isAndroid = false;
    bool isFlutter = false;
    
    // 需要學習的基礎語言
    void needLearnBaseProgram () {
      if (isIOS) {
        print('Need Learn C Firstly');
      } else if (isAndroid) {
        print('Need Learn Java Firstly');
      } else if (isFlutter) {
        print('Need Learn Dart Firstly');
      } else {
        print('May be need Learn Other Language');
      }
    }
    
  }

class FlutterDeveloper with Developer {
	String name;
	FlutterDeveloper(String name) {
		isFlutter = true;
		this.name = name;
	}
}
  
複製程式碼

使用的相關程式碼:

    FlutterDeveloper flutterDeveloper = FlutterDeveloper('FlutterEnginerName');
    flutterDeveloper.needLearnBaseProgram();
    
    // 輸出結果: flutter: Need Learn Dart Firstly
複製程式碼

注意事項: 當在if else 場景下使用 bool 型別變數的時候,需要注意bool變數是否賦值過了,否則會有類似如下的異常資訊。

flutter: The following assertion was thrown while handling a gesture:
flutter: Failed assertion: boolean expression must not be null
複製程式碼

4. abstract

使用 abstract 修飾的類 記為抽象類。抽象類用於定義介面 及部分實現。

筆者舉了如下例子:

建立了People 類,並且宣告瞭 String skinColor();的抽象方法,建立並實現了 void ability() 方法;

abstract class People {
  String skinColor();
  void ability() {
    print('All can Communicate');
  }

}

class YellowPeople extends People {
  @override
  String skinColor() {
    String color = 'Yellow';
    print(color);
    return color;
  }
}

class BlackPeople extends People {
  @override
    skinColor() {
      String color = 'black';
      print(color);
      return color;
    }
}

class WhitePeople extends People {
@override
  skinColor() {
    String color = 'White';
    print(color);
    return color;
  }
}
複製程式碼

下邊是使用示例,及相應的輸出結果。


YellowPeople yellowPeople = YellowPeople();
yellowPeople.ability();
yellowPeople.skinColor();

WhitePeople whitePeople = WhitePeople();
whitePeople.ability();
whitePeople.skinColor();

BlackPeople blackPeople = BlackPeople();
blackPeople.ability();
blackPeople.skinColor();
    
// 輸出結果:
flutter: All can Communicate
flutter: Yellow
flutter: All can Communicate
flutter: White
flutter: All can Communicate
flutter: black

複製程式碼
  • 抽象類不能建立例項。
  • 抽象方法為沒有方法體的方法。只有抽象類中可以寫抽象方法,其他普通類不可以。
    • 例:如果BlackPeople的skinColor 沒有方法體即沒有實現,則會報錯如下:'skinColor' must have a method body because 'BlackPeople' isn't abstract. Try making 'BlackPeople' abstract, or adding a body to 'skinColor'.
  • 繼承了抽象類的子類必須實現抽象方法
    • 以WhitePeople 為例,如果不實現skinColor 方法會報出如下錯誤:
      • Missing concrete implementation of People.skinColor.
      • Try implementing the missing method, or make the class abstract.

5. override

"5.1 override 運算子"及override toString

這裡筆者對override 運算子新增了引號。至於原因,等大家看完了下邊的內容之後,便會了解筆者的用意。下文提到的override和重寫是一個意思。

先看下運算子重寫的示例程式碼:

Vector 類,重寫了+ 運算子和減運算子,以達到Vector可以直接進行加減的目的。筆者還重寫了Vector類的toString 方法,便於檢視Vector的x、y值。

class Vector {
  final int x;
  final int y;
  
  const Vector(this.x, this.y);

  Vector operator +(Vector v) {
    return Vector(x + v.x, y + v.y);
  }

  Vector operator -(Vector v) {
    return Vector(x - v.x, y - v.y);
  }
  
  @override
  String toString() {
    return 'runtimeType:' + this.runtimeType.toString() + ',x:' + x.toString() +',y:' + y.toString();
  }
  
}

複製程式碼

使用Vector的+、-運算子,及重寫toString後,使用Vector的示例程式碼及輸出結果如下:

    Vector v1 = Vector(1, 1);
    Vector v2 = Vector(2, 2);
    Vector v3 = v1 + v2;
    Vector v0 = v2 - v1;
    print(v0);
    print(v3);

// 輸出結果: 
flutter: runtimeType:Vector,x:1,y:1
flutter: runtimeType:Vector,x:3,y:3

複製程式碼

重寫toString的效果是:可控制print的物件的內容及格式。 這一點便於非除錯環境下檢視一些具體錯誤資訊。

上文筆者提到了重寫運算子是加引號的原因如下:在筆者看來,運算子的重寫有點跑題了。重寫toString才算是重寫。重寫的toString的返回值、方法名和引數和父類Object都一樣。如大家有不同理解,歡迎討論。

參考學習網址


小編微信:可加並拉入《QiShare技術交流群》。

Dart基礎(四)

關注我們的途徑有:
QiShare(簡書)
QiShare(掘金)
QiShare(知乎)
QiShare(GitHub)
QiShare(CocoaChina)
QiShare(StackOverflow)
QiShare(微信公眾號)

推薦文章:
Dart基礎(一)
Dart基礎(二)
Dart基礎(三)
iOS 簡訊驗證碼倒數計時按鈕
iOS 環境變數配置
iOS 中處理定時任務的常用方法
演算法小專欄:貪心演算法
iOS 快速實現分頁介面的搭建
iOS 中的介面旋轉
奇舞週刊

相關文章