[Flutter]flutter基礎(chǔ)之Dart語言基礎(chǔ)(四)

一、運算符重載

Dart 支持運算符重載,使用 operator 關(guān)鍵字,語法格式為:operator 運算符 ,如下,重載加法運算符:

main(){
  Vector v1 = Vector(1, 2);
  Vector v2 = Vector(2, 3);
  Vector v3 = v1    + v2;
  print("${v3.x} ${v3.y}");   //輸出 3 5
}

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

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

一、模塊的使用

有點規(guī)模的工程一般都會有很多個文件和模塊組成,甚至簡單的程序也不會是單個文件組成的。在 Dart 中,每一個 .dart 文件就是一個模塊,稱為庫??梢詫⒉煌墓δ芊盅b在不同的庫中,以便在不同的文件中使用同一庫的功能函數(shù),減少代碼量,可以使用 import 關(guān)鍵字進行文件導入。

在 Dart 中,庫(模塊)類型分為三種:自定義庫、系統(tǒng)庫(Dart 提供的庫文件)、第三方庫。

1. 引入自定義庫

引入自定義庫(自己創(chuàng)建的 .dart 文件)的方式如下,如在當前文件夾新建一個 common 文件夾為公共類文件夾,在文件夾中新建 common.dart 文件,內(nèi)容如下:

void description(){
  print("描述內(nèi)容");
}

main 方法所在文件引入 common.dart 文件并使用方式如下:

import 'common/common.dart';

main(){
  description();
}

通過 import 導入文件后,即可使用文件中的方法等內(nèi)容。當然也可以在自定義庫內(nèi)新建其他 class 等。引入的格式為 import '路徑/文件名.dart' 。

2. 引入系統(tǒng)庫

系統(tǒng)庫是由 Dart 事先提供的庫,包含了一些文件或其他內(nèi)容的處理方法,如 io 、math 等文件,引入方式如下:

import 'dart:math';

main(){
  num maxNumber = max(10, 20);
  print(maxNumber);
}

max 為 系統(tǒng) math 庫提供的比較兩個數(shù)字并返回較大數(shù)字的函數(shù),引入庫后可直接使用。引入格式為:import 'dart:文件名' 。

3. 引入第三方庫

第三方庫為其他開發(fā)者或公司等寫好的具有一定功能的函數(shù)庫,可以在 Dart 提供的官方平臺尋找需要的庫,地址為:https://pub.dev 。比如我們前面使用過的 meta.dart 文件,可以在網(wǎng)站中直接搜 meta 即可。結(jié)果如下圖:

2020224_12_15.png

點擊 Installing ,進入安裝說明頁,如下圖:

2020224_12_15.jpg

本頁列出了包的安裝方式,可以通過命令行和配置文件方式引入,這里介紹下配置文件引入的方法。對于 Flutter 項目,創(chuàng)建工程后在文件的頂層目錄會存在一個名為 pubspec.yaml 的配置文件。在沒有此文件的工程中可以手動在頂級目錄添加此文件,里面包含一些包的依賴和其他信息,具體可以參考:https://dart.dev/tools/pub/pubspec 。只需將按照上圖的步驟1,將包名和對應的版本號添加到 dependencies 即可。編譯器會自動下載所需依賴包,開發(fā)者只需在工程文件中,通過 package 方式引入即可,如沒有自動下載依賴,可以查看官方的具體信息,進入項目所在文件目錄,使用 pub get 命令下載即可。 使用如下方式導入:

import 'package:meta/meta.dart';
4. 私有屬性

對于私有屬性,前面文章簡單介紹過。在 Dart 中,沒有關(guān)鍵字 public 、protectedprivate 。如果標識符以下劃線(_)開頭,則表示該標識符私有屬性,只能在定義標識符的庫中使用(定義標識符的當前.dart 文件)。

5. 指定庫前綴

當引入的不同庫中存在相同的類名、方法名或其他相同名稱時,有時會出現(xiàn)沖突的問題,在 Dart 中,通過指定一個或多個庫前綴的方式解決,使用 as 關(guān)鍵字。如 common.dartcommon1.dart 中同時存在名為 description 的方法,內(nèi)容分別如下:

//common.dart
void description(){
  print("common的描述內(nèi)容");
}

//common1.dart
void description(){
  print("common1的描述內(nèi)容");
}

main 方法所在文件進行引入:

import 'common/common.dart';
import 'common/common1.dart';

main(){
  // description();  此時調(diào)用會拋出兩個庫文件存在同一方法的異常
}

此時,需要至少指定其中一個庫的前綴或部分引入的方式解決問題,使用前綴如下:

import 'common/common.dart';
import 'common/common1.dart' as com1;

main(){
  description();  
  com1.description();
}

這里為 common1.dart 指定了 com1 前綴,調(diào)用方法或其他內(nèi)容時,需要加上前綴進行調(diào)用即可。類似于其他語言的命名空間概念。

6. 部分引入庫

當一個庫中包含很多解決問題的方法時,引入庫但只會用到其中很少一部分內(nèi)容或不想用某些方法內(nèi)容,可以使用 showhide 關(guān)鍵字進行指定。在 common.dart 文件中添加方法 printInfo ,common.dart 內(nèi)容如下:

void description(){
  print("common的描述內(nèi)容");
}

void printInfo() {
  print("打印指定信息");
}

使用 show 關(guān)鍵字指定只導入 printInfo 方法如下:

import 'common/common.dart' show printInfo;

main(){
  // description();    //調(diào)用description會拋出異常
  printInfo();    //輸出 打印指定信息
}

使用 hide 關(guān)鍵字指定不導入部分如下:

import 'common/common.dart' hide printInfo;

main(){
  description();   
  // printInfo();  //調(diào)用會報未定義錯誤 
}

show 多個可用逗號做分隔。另外,Dart 中還支持惰性加載庫,使用關(guān)鍵字 deferred ,但是 Flutter 并不支持延遲,所以不多做介紹,有興趣的朋友可以自行了解。

二、泛型

第一篇文章介紹過泛型的基本使用,使用泛型是為了解決類型安全問題以及重復代碼的問題。泛型,也就是通用類型,在前面的文章中使用過泛型。例如,當創(chuàng)建一個只能包含字符串的列表(List)時,如不通過泛型執(zhí)行數(shù)據(jù)類型,則創(chuàng)建的列表為動態(tài)類型,如下:

main(){
  List lst = List();
  print(lst.runtimeType);  //輸出 List<dynamic>
}

如果列表是為了特定的問題而創(chuàng)建的只能包含字符串的類型(可能需要對列表內(nèi)的數(shù)據(jù)進行特定的字符串處理),此時如果數(shù)組內(nèi)添加了非字符串類型數(shù)據(jù),調(diào)用字符串處理方法就會出現(xiàn)異常,所以需要使用泛型對添加的數(shù)據(jù)類型做限定,如下:

main(){
  List lst = List<String>();
  print(lst.runtimeType);   //輸出 List<String>
}

此時如果列表添加其他類型數(shù)據(jù)就會拋出異常,如法進行添加。

也可以使用泛型來減少重復代碼,如下:

main(){
  var student1 = StudentInt(1);
  var student2 = StudentString("hike");

  student1.study();             //輸出 1 號學生在學習
  student2.study();             //輸出 hike 在學習
}

class StudentInt {
  int stuId;
  StudentInt(this.stuId);

  void study(){
    print("$stuId 號學生在學習");
  }
}

class StudentString {
  String name;
  StudentString(this.name);

  void study(){
    print("$name 在學習");
  }
}

上述代碼,分別為通過學號和學生姓名來創(chuàng)建學生,創(chuàng)建了兩個類,通過泛型修改如下:

main(){
  var student1 = Student(1);
  var student2 = Student("hike");

  student1.study();             //輸出 1 號學生在學習
  student2.study();             //輸出 hike 在學習
}

class Student<T> {
  T stuIdentifier;
  Student(this.stuIdentifier);

  void study(){
    if(stuIdentifier is num) {
      print("$stuIdentifier 號學生在學習");
    }else if(stuIdentifier is String) {
      print("$stuIdentifier 在學習");
    }
  }
}

通過泛型 <T> 可以有效減少代碼的重復量,通過傳入不同的參數(shù)類型區(qū)分不同的執(zhí)行結(jié)果。其中 T 為標識符,為類型占位,類型為傳入數(shù)據(jù)的數(shù)據(jù)類型,在運行時確定。

泛型的另一個用途是用來約束范圍,可以用來用在繼承類類型的限制,如下:

main(){;
  var student = Coach<Student>(new Student("學生 在講話"));
  student.coachMethod();                                //學生在講話
}

class Coach<T extends Person> {
  T coachData;
  Coach(this.coachData);

  coachMethod() {
    coachData.say();
  }
}

class Student extends Person {
  Student(String name) : super(name);
}

class Teacher extends Person {
  Teacher(name) : super(name);
}

class Person {
  String profession;
  Person(this.profession);
  void say(){
    print("$profession 在講話");
  }
}

這里定義了輔導類(Coach),泛型約束了傳入的類必須是繼承自 Person 類,main 中的定義方法又約束了具體的類的類型,如果傳入其他類進行初始化操作則會報錯。

也可以在函數(shù)中使用泛型,如下:

main(){;
  print(lastString([1, 2, 3]));
}

T lastString<T>(List<T> lst) {
  if(lst.length <= 0){
    print("長度有誤");
    return null;
  }
  return lst.first;
}

T lastString<T>(List<T> lst) 中,第一個 T 代表返回值類型,第二個 <T> 表示可以在函數(shù)的多個地方使用類型為 T 的參數(shù),如果不加此參數(shù)會報錯,第三個為列表中的參數(shù)類型。

三、異步編程

在程序設(shè)計中,為了不影響用戶的使用,都會對耗時的任務(wù)做異步處理,以防止對用戶界面或功能造成假死、卡頓等現(xiàn)象。在 Dart 中, 使用 asyncawait 來做異步編程,也可以使用 Future API。

1. async 與 await

await 必須在 async 函數(shù)中使用。比如,定義如下函數(shù):

getNetworkData() {
 //  var data = await "獲取的網(wǎng)絡(luò)數(shù)據(jù)"; //次處代碼會報錯,await 必須使用在 async 函數(shù)中
}

對上述代碼做如下修改:

main(){;
  getNetworkData();
  print("執(zhí)行完畢");
}

getNetworkData() async {
  var data = await "獲取的網(wǎng)絡(luò)數(shù)據(jù)";
  print("數(shù)據(jù):$data");
}

輸出結(jié)果:

執(zhí)行完畢
數(shù)據(jù):獲取的網(wǎng)絡(luò)數(shù)據(jù)

如果去掉 await 關(guān)鍵字修飾,做如下修改:

main(){;
  getNetworkData();
  print("執(zhí)行完畢");
}

getNetworkData() async {
  var data = "獲取的網(wǎng)絡(luò)數(shù)據(jù)";
  print("數(shù)據(jù):$data");
}

輸出結(jié)果:

數(shù)據(jù):獲取的網(wǎng)絡(luò)數(shù)據(jù)
執(zhí)行完畢

可以發(fā)現(xiàn)當去掉 await 修飾后,函數(shù)的執(zhí)行過程即為調(diào)用過程,順序執(zhí)行(即便在 getNetworkData 中有耗時操作 )。所以 asyncawait 需要一起使用,需要之后執(zhí)行的處理使用 await 修飾。異步操作,不會等待操作執(zhí)行完畢,而是繼續(xù)執(zhí)行后面的任務(wù)。

可以同時使用多個 await ,如下:

getNetworkData() async {
  var data =  await "獲取的網(wǎng)絡(luò)數(shù)據(jù)";
  print("數(shù)據(jù):$data");

  var proData = await "處理數(shù)據(jù)";
  print("處理:$proData");
}
2. 回調(diào)

當從網(wǎng)絡(luò)獲取到數(shù)據(jù)后,需要對數(shù)據(jù)進行本地處理后進行UI渲染,Dart 中可以使用回調(diào)函數(shù)的處理方式處理此問題,如下:

main(){;
  getNetworkData(renderUI);
  print("執(zhí)行完畢");
}

getNetworkData(methodCallBack) async {
  var data =  await "獲取的網(wǎng)絡(luò)數(shù)據(jù)";
  methodCallBack(data);
}

renderUI(var data){
  print("$data 進行UI渲染");
}
2. Future API

對于被 async 修飾的異步函數(shù),其返回值類型為 Future ,無論是否顯式執(zhí)行返回值類型。對于沒有返回值的異步函數(shù),應使用 Feture<void> 作為返回值類型。如下:

Future<void> getNetworkData() async {
  var data =  await "獲取的網(wǎng)絡(luò)數(shù)據(jù)";
  print("數(shù)據(jù):$data");
}

使用 Futurethen 方式處理函數(shù)回調(diào),如下:

main(){
  var future = getNetworkData();
  future.then((data){;
    renderUI(data);
  });
}

getNetworkData() async {
  var data =  await "獲取的網(wǎng)絡(luò)數(shù)據(jù)";
  return data;
}

renderUI(var data){
  print("$data 進行UI渲染");
}

官方建議,如果能使用 await 執(zhí)行異步操作盡量使用此方式。Future 的 then 方法主要用于當有多個異步任務(wù)有依賴關(guān)系時使用。

關(guān)于更多 Future API的使用,可見官方文檔:https://dart.dev/guides/libraries/library-tour#dartasync---asynchronous-programming

3. 流處理

如果數(shù)據(jù)以流的方式進行處理,有兩種方式,一種是使用 asyncawait for 處理,另外一種是使用 stream API。詳細見官方文檔:https://dart.dev/guides/libraries/library-tour#stream

四、可調(diào)用類

可調(diào)用類的作用是使類的實例可以像函數(shù)一樣使用,需要在類中實現(xiàn) call()方法,如下:

main(){
  var student = Student();
  var des = student("hike", 11);
  print(des);
}

class Student {
  call(String name, int age) {
    return "$age 的學生 $name 在學習!";
  }
}

五. Typedefs

在當前的 Dart 中,可以為函數(shù)指定別名(其他類型暫時不可以),使用 typedef 關(guān)鍵字。Dart 中的此功能是一種函數(shù)匹配,就官網(wǎng)的例子來說看似有點雞肋,看如下例子:

main(){
  var mAdd = MethodAdd(addFunction);
  var result= mAdd.func(1, 2);
  print(result);     //輸出 3
  print(mAdd.func is Function);  //輸出 true
}

class MethodAdd {
  Function func;
  MethodAdd(this.func);
}

num addFunction(num a, num b){
  return a + b;
}

定義一個 Function 類型參數(shù)作為類 MethodAdd 的構(gòu)造函數(shù)參數(shù),傳入 addFunction 方法做加法運算并得出結(jié)果。使用 typedef 定義別名方式如下:

typedef Add = num Function(num a, num b);

main(){
  var mAdd = MethodAdd(addFunction);
  var result= mAdd.func(1, 2);
  print(result);                            //輸出 3
  print(mAdd.func is Function); //輸出 true
  print(mAdd.func is Add);          //輸出 true
}

class MethodAdd {
  Add func;
  MethodAdd(this.func);
}

num addFunction(num a, num b){
  return a + b;
}

上述代碼最終的區(qū)別就在于最后對類型的具體判斷,未使用 typedef 定義別名時,僅能判斷傳入的是一個函數(shù),使用別名后,因為定義了具體類型(這里是加法),可以具體判斷到加法,傳入其他類型,也是如此,也就是提供了一種類型檢查機制。

六、Metadata

Dart 中,使用元數(shù)據(jù)來提供有關(guān)代碼的其他信息。元數(shù)據(jù)注解使用字符 @ 開頭,其后是對編譯時常量的引用。Dart 中提供的批注有:@deprecated 、@override 、@proxy 。

@override 前面說過,用來指示其標識的代碼為重寫父類的代碼。

@deprecated 表示被標識的代碼被棄用,應使用其他替代代碼,但依然可以執(zhí)行,直到下次版本更新。

main(){
  Student student = Student();
  student.studyData();
  student.study();
}

class Student extends Person{
  @override
  void eat() {
    print("學生吃");
  }

  @deprecated
  void studyData() {
    study(); //studyData 將被棄用,使用study方法代替
  }

  void study() {
    print("學習");
  }
}

class Person {
  void eat(){
    print("吃");
  }
}

也可以自定義元數(shù)據(jù)注解,新建一個名為 customAnnotation.dart 的文件,創(chuàng)建自定義注解并不一定需要單獨創(chuàng)建一個文件,這里只是個例子,如一個注解只在本類使用,在類中創(chuàng)建即可(不知道是否有意義,但是這樣做是可以的)。如果需要創(chuàng)建一個共享的,在多個列中都使用的注解,則單獨創(chuàng)建一個 .dart 文件。我們創(chuàng)建的文件內(nèi)容如下:

library studyDescription;

/**
 * 這是對學習的描述
*/
const StudyDes stuDes = StudyDes();

class StudyDes {
  const StudyDes();
}

這是一個用來描述學習的注解(只是例子,沒有任何意義,這是根據(jù)官方注解的樣子仿寫的)。第一行使用 library 指令聲明這是一個正式的 Dart 庫(無此聲明也可以),名字為 studyDescription 。注釋格式為文檔注釋,創(chuàng)建的注解為 stuDes 。定義注解的類的構(gòu)造函數(shù)必須為常量構(gòu)造函數(shù),關(guān)于常量構(gòu)造函數(shù)的說明上文有過說明。 使用方式如下:

import 'customAnnotation.dart';

main(){
  Student student = Student();
  student.study();
}

class Student{
  @stuDes
  void study() {
    print("學習");
  }
}

在編譯器中,當鼠標放在 import 'customAnnotation.dart'; 上靜止不動時,會顯示 library 定義的正式庫的名字,鼠標放在 stuDes 上會提示注解的注釋信息。這個注解是無參數(shù)的,也可以定義有參數(shù)的,與定義類的方式?jīng)]什么區(qū)別,這里不再贅述。

元數(shù)據(jù)可以出現(xiàn)在庫(library),類(class),typedef,類型參數(shù)(type parameter),構(gòu)造函數(shù)(constructor),工廠(factory),函數(shù)(function),字段(field),參數(shù)(parameter)或變量聲明(variable declaration)之前,也可以出現(xiàn)在導入或?qū)С鲋噶钪?。您可以在運行時使用反射來檢索元數(shù)據(jù)

至此,Dart 基礎(chǔ)語法部分完畢。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容