动态

详情 返回 返回

Flutter/Dart第14天:Dart類詳解 - 动态 详情

Dart官方文檔:https://dart.dev/language/classes

重要説明:本博客基於Dart官網文檔,但並不是簡單的對官網進行翻譯,在覆蓋核心功能情況下,我會根據個人研發經驗,加入自己的一些擴展問題和場景驗證。

Dart類

Dart語言基於Mixin繼承,是一門面向對象語言。任何對象都是某個類的實例,除Null之外,Object類其他所有類的父類。

Mixin繼承:Dart語言和Java語言一樣,類只能是單繼承。但通過Mixin,一個類的代碼可以在多個類層次結構中複用(有關Minxin的詳細説明見之前文章:https://ntopic.cn/p/2023093001)。

方法擴展:在不改變原有類和增加子類的情況之下,通過Dart的方法擴展,可以給類增加功能的一種方法(這個特性在Flutter發佈的庫特別有用)。

類修飾符:可以讓我們可控制一個庫如果定義子類。

類成員(方法和變量)

對象是由函數和數據組成,分別代碼方法和變量。我們通過對象.方法或者對象.變量的方法來訪問對象方法和變量。當對象可能為null時,通過對象.?訪問方法和變量的方式可防止異常發生。

// 定義對象
var p = Point(2, 2);

// 獲取對象變量`y`
assert(p.y == 2);

// 調用對象方法:`distanceTo()`
double distance = p.distanceTo(Point(4, 4));

// 當對象`p`非空時,`a`值為變量`y`;否則`a`值為null
var a = p?.y;

類構造函數

在前面學習中,我們對構造函數有初步認識:https://ntopic.cn/p/2023092401

如下代碼樣例,可以通過主構造函數和命名構造函數創建一個對象;構造函數之前,我們也可以增加可選的new關鍵字:

var p1 = Point(2, 2);
var p2 = Point.fromJson({'x': 1, 'y': 2});

// 同上等價代碼,可選的`new`關鍵字
var p1 = new Point(2, 2);
var p2 = new Point.fromJson({'x': 1, 'y': 2});

當類的變量都用final不可變修飾時,我們可以構造常量對象:

var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);

// 對象`a`和`b`相等
assert(identical(a, b));

對於一個常量上下文,我們可以去掉構造函數之前的const關鍵字。如下代碼樣例,我們定義的是一個常量Map(上下文),那麼Map元素的構造器就可以省略const關鍵字:

const pointAndLine = const {
  'point': const [const ImmutablePoint(0, 0)],
  'line': const [const ImmutablePoint(1, 10), const ImmutablePoint(-2, 11)],
};

// 同上等價代碼,可省略`const`關鍵字
const pointAndLine = {
  'point': [ImmutablePoint(0, 0)],
  'line': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)],
};

如果一個對象沒有常量上下文,且沒有使用const修飾構造器,那麼它創建的是一個非常量對象:

var a = const ImmutablePoint(1, 1);
var b = ImmutablePoint(1, 1);

// `a`是常量對象,`b`不是常量對象,因此它們不相等!
assert(!identical(a, b));

獲取對象類型

通過對象.runtimeType屬性,返回對象的Type對象。一般情況下,我們通過對象 is Type的方法,檢測某個對象是否屬於某個類型,而不是使用對象.runtimeType == Type比較方式:

print('The type of a is ${a.runtimeType}');

var a = 'Hello NTopicCN';
assert(a.runtimeType == String);
assert(a is String);

實例變量

如下代碼定義樣例,申明實例變量,實例變量的默認值為null

class Point {
  double? x; // 默認值:null
  double? y; // 默認值:null
  double z = 0; // 默認值:0
}

所有的實例變量都隱含有一個getter方法,包括final修飾的變量未使用final修飾的變量late final修飾的變量(賦值和未賦值)等,都有getter方法。

如下代碼樣例,幾種實例變量修飾和訪問的方法:

class Point {
  double? x;
  double? y;
}

void main() {
  var point = Point();
  point.x = 4; // `setter`方法賦值
  assert(point.x == 4); // `getter`方法取值
  assert(point.y == null); // 默認值為`null`
}

class ProfileMark {
  final String name;
  final DateTime start = DateTime.now();

  // 主構造函數
  ProfileMark(this.name);

  // 命名構造函數,同時`name`設置初始值
  ProfileMark.unnamed() : name = '';
}

隱性接口

在Dart中,每個類都隱含的定義了一個接口,這個接口包含了該類的所有實例成員和該類實現的所有的其他接口。

假設我們定義了一個類A,它需要支持類B的API(構造函數不是API),但是類A的定義並不是繼承類B,那麼類A需要實現B接口。

// `Person`類,也是`Persion`接口,包含`greet()`方法
class Person {
  // 屬於接口的一部分,但是對外不可見
  final String _name;

  // 構造函數,不屬於接口一部分
  Person(this._name);

  // 普通方法,屬於接口一部分
  String greet(String who) => 'Hello, $who. I am $_name.';
}

// 實現`Person`接口
class Impostor implements Person {
  String get _name => '';

  String greet(String who) => 'Hi $who. Do you know who I am?';
}

String greetBob(Person person) => person.greet('Bob');

void main() {
  print(greetBob(Person('Kathy'))); // Hello, Bob. I am Kathy.
  print(greetBob(Impostor())); // Hi Bob. Do you know who I am?
}

類變量和方法

static關鍵字,可以定義類變量和方法(Java中成為靜態變量和靜態方法)。

如下代碼樣例,定義和使用一個類變量:

class Queue {
  static const initialCapacity = 16;
  // ···
}

void main() {
  assert(Queue.initialCapacity == 16);
}

如下代碼樣例,定義和使用類方法:

import 'dart:math';

class Point {
  double x, y;
  Point(this.x, this.y);

  static double distanceBetween(Point a, Point b) {
    var dx = a.x - b.x;
    var dy = a.y - b.y;
    return sqrt(dx * dx + dy * dy);
  }
}

void main() {
  var a = Point(2, 2);
  var b = Point(4, 4);
  var distance = Point.distanceBetween(a, b);
  assert(2.8 < distance && distance < 2.9);
  print(distance);
}

最佳實踐:對於一些常用的工具方法,建議使用頂級方法代替類變量。

類方法可以用作編譯期常量,比如:我們可以把一個類方法當作參數傳遞給常量構造器。


我的本博客原地址:https://ntopic.cn/p/2023102001


Add a new 评论

Some HTML is okay.