Flutter/Dart第15天:Dart類構造函數

来源:https://www.cnblogs.com/obullxl/archive/2023/10/22/NTopic2023102101.html
-Advertisement-
Play Games

我們通過類構造函數來創建對象,上文(第14天)我們學到,與Java不同,創建Dart對象時可以省略構造函數之前的new關鍵字。同時,Dart語言除預設構造函數外,還有命名構造函數,重定向構造函數,常量構造函數和工廠構造函數等…… ...


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

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

如下代碼樣例,和Java類似,最常用的生成式構造函數:

class Point {
  double x = 0;
  double y = 0;

  Point(double x, double y) {
    this.x = x;
    this.y = y;
  }
}

最佳實戰:在Dart中,僅當命名衝突時,才使用this關鍵字,否則一般可以省略this關鍵字。

初始化參數列表

如上最常用的構造函數,Dart可以進一步優化如下初始化參數形式。同時,也可以為非空變數設置預設值。

class Point {
  final double x;
  final double y;

  // 在構造函數體執行之前,初始化實例變數
  Point(this.x, this.y);
}

預設構造函數

和Java類似,類如果沒有申明構造函數,那麼它會有個預設的構造函數。預設構造函數沒有入參,它只會調用父類的沒有入參的構造函數。

構造函數無法繼承

子類無法繼承父類的構造函數,如果子類沒有申明構造函數,那麼這個子類就只有預設構造函數(無論父類是否有其他構造函數)。

命名構造函數

在Dart中,通過命名構造函數,可以為類提供多個構造函數,並且在創建對象時更加清晰。

const double xOrigin = 0;
const double yOrigin = 0;

class Point {
  final double x;
  final double y;

  Point(this.x, this.y);

  // `origin`命名構造函數
  Point.origin()
      : x = xOrigin,
        y = yOrigin;
}

特別註意:如上節提到,子類無法繼承父類的構造函數,包括父類的命名構造函數。如果子類想使用父類的某個命名構造函數,那麼子類必須實現該命名構造函數。

調用父類構造函數

預設情況下,子類無入參的非命名構造函數會調用父類的無入參的非命名構造函數。父類的構造函數在構造函數體之前被調用。如果有初始化參數列表,那麼初始化參數列表在父類構造函數調用之前被調用。

構造函數相關的調用順序如下:

  • 初始化參數列表
  • 父類無入參的構造函數
  • 子類無入參的構造函數

特別註意:如果父類沒有通過無入參且非命名的構造函數,那麼我們必須手工調用父類的一個構造函數,通過冒號:後面緊跟父類構造函數。

如下代碼樣例,Person是父類,它僅申明瞭一個命名構造參數。Employee是繼承Person的子類,由於父類沒有申明無入參非命名的構造函數,因此在它構造函數都必須手工調用父類的某個構造函數。如命名構造函數fromJson後面,通過冒號:調用了父類的命名構造函數。

// 未申明:無入參、非命名的構造函數
class Person {
  String? firstName;

  Person.fromJson(Map data) {
    print('in Person');
  }
}

class Employee extends Person {
  // 手工調用父類的構造函數:super.fromJson()
  Employee.fromJson(super.data) : super.fromJson() {
    print('in Employee');
  }
}

void main() {
  var employee = Employee.fromJson({});
  print(employee);
  // 結果:
  // in Person
  // in Employee
  // Instance of 'Employee'
}

特別註意:由於構造函數參數是在調用構造函數之前計算,因此構造函數的參數可以是表達式,如函數調用等。父類的構造函數不能使用this.關鍵字,因為參數可以是表達式、靜態函數等,並不一定是類實例。

class Employee extends Person {
  Employee() : super.fromJson(fetchDefaultData());
  // ···
}

手工調用父類的構造函數,然後逐一設置入參比較繁瑣,如果我們想要簡化,那麼可以父類的初始值構造函數。這個功能不能與重定向構造函數一起使用(因為語法衝突)。

class Vector2d {
  final double x;
  final double y;

  Vector2d(this.x, this.y);
}

class Vector3d extends Vector2d {
  final double z;

  // 預設情況下,我們的使用方法:
  // Vector3d(final double x, final double y, this.z) : super(x, y);
  
  // 簡化版本:
  Vector3d(super.x, super.y, this.z);
}

如下代碼樣例,父類的初始化構造函數可以通過命名參數調用。

class Vector2d {
  // ...

  Vector2d.named({required this.x, required this.y});
}

class Vector3d extends Vector2d {
  // ...

  Vector3d.yzPlane({required super.y, required this.z}) : super.named(x: 0);

  // 等價調用
  // Vector3d.yzPlane({required double y, required this.z}) : super.named(x: 0, y: y);
}

初始化列表

在構造函數執行之前,我們可以調用父類的構造函數,還可以初始化實例變數。實例變數初始化通過逗號分隔。

Point.fromJson(Map<String, double> json)
    : x = json['x']!,
      y = json['y']! {
  print('In Point.fromJson(): ($x, $y)');
}

開發階段,我們可以在初始化列表中增加斷言:

Point.withAssert(this.x, this.y) : assert(x >= 0) {
  print('In Point.withAssert(): ($x, $y)');
}

初始化列表在設置final不可變數時非常有用:

import 'dart:math';

class Point {
  final double x;
  final double y;
  final double distanceFromOrigin;

  Point(double x, double y)
      : x = x,
        y = y,
        distanceFromOrigin = sqrt(x * x + y * y);
}

void main() {
  var p = Point(2, 3);
  print(p.distanceFromOrigin);
  // 輸出:3.605551275463989
}

重定向構造函數

重定向構造函數,就是使用類的其他的構造函數,重定向到的構造函數使用this關鍵字:

class Point {
  double x, y;

  // 主構造函數
  Point(this.x, this.y);

  // 重定向到主構造函數
  Point.alongXAxis(double x) : this(x, 0);
}

常量構造函數

如果對象的數據不會改變,這些對象可以作為編譯期常量。

常量構造函數要求:實例變數都是final不可變數,定義一個const修飾符的構造函數。

特別註意:上一文我們有提到常量構造函數,常量構造函數創建的對象並不一定<?都是常量(當創建的對象沒有const修飾符,或者對象不是在const常量上下文中,那麼該對象就不是常量)!

如下代碼樣例,ImmutablePoint有常量構造函數,它創建的3個對象中,前面2個是常量,後面1個並非常量。

class ImmutablePoint {
  static const ImmutablePoint origin = ImmutablePoint(0, 0);

  final double x, y;

  const ImmutablePoint(this.x, this.y);
}

// `a`和`b`對象是常量,且它們屬於同一個實例
var a = const ImmutablePoint(1, 2);
var b = const ImmutablePoint(1, 2);

assert(identical(a, b));

// `c`對象並非常量,它也和`a`或者`b`不是同一個實例
var c = ImmutablePoint(1, 2);
assert(!identical(a, b));

工廠構造函數

在一個不總是創建實例的類中,使用factory關鍵字實現一個構造函數,即為工廠構造函數。

工廠構造函數常用使用場景:如通過構造函數,從緩存獲取對象,或者創建其子類,或者創建不可變常量但是又不想提供初始化參數列表等。

如下代碼樣例,Logger工廠構造函數優先從緩存獲取對象,而Logger.fromJson()工廠構造函數則初始化了一個final不可實例變數:

class Logger {
  final String name;
  bool mute = false;

  static final Map<String, Logger> _cache = <String, Logger>{};

  // 優先從緩存獲取對象,如果不存在則新增
  factory Logger(String name) {
    print('預設構造函數:$name');
    return _cache.putIfAbsent(name, () => Logger._internal(name));
  }

  // 命名構造函數,通過工廠構造函數獲取對象(緩存,或新增)
  factory Logger.fromJson(Map<String, Object> json) {
    print('命名構造函數:$json');
    return Logger(json['name'].toString());
  }

  Logger._internal(this.name);

  void log(String msg) {
    if (!mute) print(msg);
  }
}

工廠構造函數的使用,和普通構造函數無本質區別:

var logger = Logger('UI');
logger.log('Hi NTopicCN.');
// 結果:
// 預設構造函數:UI
// Hi NTopicCN.

var loggerJson = Logger.fromJson({'name': 'UI'});
loggerJson.log('Hello Logger.');
// 結果:
// 命名構造函數:{name: UI}
// 預設構造函數:UI
// Hello Logger.

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


本文作者:奔跑的蝸牛,轉載請註明原文鏈接:https://ntopic.cn


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 安裝Ubuntu Linux元信息 兩台機器,每台機器兩台Ubuntu Ubuntu版本:ubuntu-22.04.3-desktop-amd64.iso 處理器數量2,每個處理器的核心數量2,總處理器核心數量4 單個虛擬機記憶體8192MB(8G),最大磁碟大小30G 參考鏈接 清華大學開源軟體鏡像 ...
  • 公司有一個新需求,在原來項目基礎上開發,項目中使用 Ant Design Vue,版本是 1.X ,在此記錄下遇到的問題;對於沒有使用過或者使用程度不深的同學來說,希望可以幫助你在開發中遇到問題時有個參考。對於已經熟練使用的同學,可能這些問題都遇到過,歡迎大家在評論區補充。 1、實現對下拉框顯示的所 ...
  • 這是我用於複習我一年前學習的JavaScript的筆記,由於一年過去了,我大概已經4~5個月沒有寫過什麼代碼,所以需要整理自己的知識體系,如果文章出錯,也希望大家評論給我改錯 JavaScript的類型 JS中的類型有以下幾種: Number(數字類型) BigInt (大數類型) String(字 ...
  • 接上一節:從零用VitePress搭建博客教程(4) – 如何自定義首頁佈局和主題樣式修改? 上一節其實我們也簡單說了自定義頁面模板,這一節更加詳細一點說明,開始之前我們要知道在vitePress中,.md的文件是可以直接編寫vue的代碼的。 比如我們現在來自定義一個前端網址導航頁面 八、自定義一些 ...
  • 2023年6月19日決定對rust做一個重新的梳理,整理今年4月份做完的rustlings,根據自己的理解來寫一份題解,記錄在此。 周折很久,因為中途經歷了推免的各種麻煩事,以及選擇資料庫作為未來研究方向後的一段適應過程,耽擱了很久。 2023年10月份秋冬季的開源操作系統訓練營又開始了,所以我回來 ...
  • 在⾯試過程中,應聘者可能想要從對公司環境的觀察以及對⾯試官的提問當中獲取公司相關的信息,以此來判斷這家公司靠不靠譜,⾃⼰值不值得去。但這種信息可能會帶有⼀定的⽚⾯性,畢竟 應聘者沒有在公司實際體驗過⼯作的內容,很難通過⼀兩次⾯試就能看出公司的實際情況。這些細節⼀般只能給予應聘者⼀點有效信息,雖然並不 ...
  • 實戰準備:要爬取的url:https://www.shicimingju.com/book/sanguoyanyi.html 實戰要求:爬取詩詞名句網站中的三國演義文章,將每章的標題和內容寫入自己的項目文件(sanguo.txt) (本次爬取使用bs4) 1 import requests 2 # ...
  • 編寫頁面這個程式設計工程師嘴上的口頭禪是在 2016 年在建行剛開始最開發的時候聽到的, 因為大學的時候學習的程式設計語言是一 C++為主,對於前端頁面這種能看到效果只是一種很難體會的概念。學習 C 語言程式設計的時候顯示在眼前的是一個命令行黑屏,學 C++, C#都是。作為大學生初學程式設計開發, ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...