Flutter之旅:Dart語法掃尾-包訪問-泛型--異常-非同步-mixin

張風捷特烈發表於2019-07-06

希望在閱讀本文之前,你已經閱讀了:
Flutter之旅:Dart的基礎語法
Flutter之旅:從原始碼賞析Dart物件導向


1、其他常用符號與關鍵字

1.1:級聯操作符:..

那Paint物件的設定來看:

---->[情景1:曾經的寫法]----
var paint = Paint();
paint.strokeCap = StrokeCap.round;
paint.style = PaintingStyle.stroke; //畫線條
paint.color = Color(0xffBBC3C5); //畫筆顏色
paint.isAntiAlias = true; //抗鋸齒
paint.filterQuality =  FilterQuality.high; //抗鋸齒

---->[情景2:級聯的寫法]----
paint
  ..strokeCap = StrokeCap.round
  ..style = PaintingStyle.stroke //畫線條
  ..color = Color(0xffBBC3C5) //畫筆顏色
  ..isAntiAlias = true //抗鋸齒
  ..filterQuality = FilterQuality.high;
複製程式碼

很簡潔,很高大上有木有,


1.2: 條件呼叫符:?.

---->[情景1:普通呼叫]----
void main() {
  var a = -5;
  print(a.abs());//5
}

---->[情景2:普通呼叫前置空,會崩掉]----
var a = 5;
a=null;
print(a.abs());//NoSuchMethodError: The method 'abs' was called on null.

---->[情景3:?.呼叫不會崩掉,只返回null]----
var a = 5;
a = null;
print(a?.abs()); //null
複製程式碼

1.3:型別判斷關鍵字、isis!as

var b=10;
print(b is String);//false
print(b is num);//true
print(b is! double);//true

String c="12315";
print((c as Comparable<String>).compareTo("a"));//-1  強制型別轉換
print((c as num).abs());//型別轉換異常
// type 'String' is not a subtype of type 'num' in type cast
複製程式碼

2.庫的使用和可見性

2.1:庫的基本使用
import 'dart:math';//匯入math內建庫
import 'package:flutter/material.dart';//根據檔案系統路徑到包
複製程式碼

2.2:類庫中的命名衝突: as 關鍵字的使用

當sin函式處於連個包中,並且兩個包都被匯入,該怎麼辦

---->[utils/color_utils.dart]----
sin(double d){
}

---->[main.dart:5]----
import 'package:toly/utils/color_utils.dart';
import 'dart:math';
void main() {
  sin(5);
}

---->[解決方案]----
import 'package:toly/utils/color_utils.dart' as myMath;
import 'dart:math';

void main() {
  myMath.sin(5);
}
複製程式碼

2.3:控制顯隱部分匯入

被隱藏的物件無法被外界訪問

import 'package:toly/utils/color_utils.dart' show sin;//只顯示sin函式
import 'package:toly/utils/color_utils.dart' hide sin;//只隱藏sin函式
複製程式碼

2.4:許可權訪問控制

需要注意的是,Dart中沒有private,public,protected修飾符。 如何做到訪問許可權控制,是個問題,預設是可以被訪問的。

---->[painter/person.dart]----
class Person{
    String name;
    int age;
    Person(this.name,this.age);
  
    say(){
    print("my name is $name and i am $age years old.");
  }
}

---->[main.dart]----
import 'package:toly/painter/person.dart';

void main() {
  var toly = Person("toly", 25);
  print(toly.age);//25
  toly.say();//my name is toly and i am 25 years old.
}
複製程式碼

Dart中規定,名稱前加下劃線可以限制外部的訪問,如下_age。
方法名,檔名也是如此,不想對外暴露,前面加下劃線即可

---->[painter/person.dart]----
class Person{
  String name;
  int _age;
  Person(this.name,this._age);

  say(){
    print("my name is $name and i am $_age years old.");
  }
}

---->[main.dart]----
void main() {
  var toly = Person("toly", 25);
  toly.say();//my name is toly and i am 25 years old.
  print(toly._age);//報錯
}
複製程式碼

2.5:libraryexport關鍵字的使用

這裡拿animation來舉例子,使用時導包:import 'package:flutter/animation.dart';
在原始碼中animation.dart只做了一個歸納暴露的動作。

library animation;

export 'src/animation/animation.dart';
export 'src/animation/animation_controller.dart';
export 'src/animation/animations.dart';
export 'src/animation/curves.dart';
export 'src/animation/listener_helpers.dart';
export 'src/animation/tween.dart';
export 'src/animation/tween_sequence.dart';
複製程式碼

3.泛型

Dart中的泛型和Java中非常相似,可以讓型別變得安全,程式碼更加優雅。

3.1:泛型的使用

拿List類來說,在類定義時類名List後加了,在使用時List就可以加一個型別。這樣的好處在於當你試圖新增其他型別的資料到該List物件中時,會報錯。這樣就是的型別變得安全。

---->[sky_engine/lib/core/list.dart:54]----
abstract class List<E> implements EfficientLengthIterable<E> {

---->[main.dart]----
void main() {
  var languages=List<String>();//定義一個泛型為String的列表
  var odd=List<int>();//定義一個泛型為int的列表
}
複製程式碼

Flutter之旅:Dart語法掃尾-包訪問-泛型--異常-非同步-mixin


3.2:List,Map,Set特殊的初始化

這並不算泛型特點,只是List,Map,Set快速初始化的寫法。
只不過看起來有些奇怪,這裡說一下,以後會常見這些寫法。

var languageList = <String>['Java', 'Dart', 'Kotlin'];
var markMap = <String,int>{'Java':100, 'Dart':80, 'Kotlin':60};
var languageSet = <String>{'Java', 'Dart','Kotlin'};
複製程式碼

3.3:泛型的限定

和Java語法一致,使用 來限定泛型的型別區域
如下面DiagnosticableNode中的泛型限定

class DiagnosticableNode<T extends Diagnosticable> extends DiagnosticsNode
複製程式碼

3.4:泛型方法

在Dart中,方法也是可以支援泛型的,比如下面的方法:
當呼叫var e = foo<int>("hello");則會報錯,改為foo<String>即可。

T add<T>(T t) {
  return t;
}
複製程式碼

4.非同步操作簡介

關於非同步,是一個挺大的話題,這裡簡單提一下,之後有詳細介紹。

4.1:非同步在Dart中的必要性

Dart是一個單執行緒的程式語言,耗時操作會造成執行緒阻塞。
就相當於我在燒開水,水燒開之前都無法進行其他動作,這顯然是不合理的。
我完全可以在燒水的時候去掃地,等水開了再去處理,需要一個Future物件用於後續處理

class Water{
  double temperature;
  Water(this.temperature);
  
  Future<Water> changeTemperature(double t) {
    for(var i=0.0;i<=t;i+=0.01){//模擬燒水過程(耗時操作)
      temperature=i;
    }
    return Future<Water> (()=>this);
  }
}
複製程式碼

4.2:asyncawait關鍵字的使用

async表示非同步,await表示等待。注意在非同步操作中返回的是Future物件
這個物件用於,後續的處理,比如水燒開後去沖水什麼的。

Future<Water> heat(Water water) async{//非同步燒水
  return await water.changeTemperature(100);//返回燒開的水的Future
}

main(){
  print("開啟燒水開關");
  heat(Water(0)).then((result){
    print('水已經燒開,現在溫度:${result.temperature},開始沖水');
  });
  print("掃地");
}
複製程式碼

Flutter之旅:Dart語法掃尾-包訪問-泛型--異常-非同步-mixin

可以卡看出,開啟燒水開關後,接著是掃地,水燒開後再衝水,這就是非同步操作。
更多的用法將在後面檔案資源的讀取時做詳細闡述。


5.異常處理

5.1:異常不處理的情況

結果由於FormatException異常,程式直接崩潰
這並不是我們想要的,直接崩潰會造成極差的使用者體驗。

void main() {
    print(str2Num("a"));//FormatException: a
}

num str2Num(String str){
   return num.parse(str);
}
複製程式碼

5.2:異常捕捉

這個和Java也是類似的,使用try...catch...finally程式碼塊
這樣異常會通過日誌列印並且程式不會崩潰退出。 其中finally程式碼塊中的語句不管異常與否,都會被執行

num str2Num(String str){
  var result= 0;
  try {
   result= num.parse(str);
  } catch (e) {
    print('發生異常:$e');
  } finally {
    print('最終會被執行的程式碼塊');
  }
  return result;
}
複製程式碼

5.3:指定異常或多個異常捕捉

使用on關鍵字,可以指定捕捉某一類異常。

num str2Num(String str){
  var result= 0;
  try {
    result= num.parse(str);
  } on FormatException catch (e) {
    print('發生Format異常:$e');
  } on IOException catch(e){
    print('發生IO異常:$e');
  } finally {
    print('最終會被執行的程式碼塊');
  }
  return result;
}
複製程式碼

6.Dart中的多繼承

知道Dart支援多繼承,我是挺驚訝的,多繼承的問題在於父類構造可能被迴圈呼叫

6.mixin的使用

6.1:子類和父類建構函式呼叫順序

通過下面的程式碼可以看出,是先呼叫父類的構造方法

class Living {
  Living(){
    print("Runner");
  }
}

class Person extends Living{
  Person(){
    print("Person");
  }
}

main(){
  Person toly = Person();
}

---->[列印結果]----
Runner
Person
複製程式碼

6.2:mixin是什麼?

首先mixin是一個定義類的關鍵字。直譯出來是混入,混合的意思
Dart為了支援多重繼承,引入了mixin關鍵字,它最大的特殊處在於:
mixin定義的類不能有構造方法,這樣可以避免繼承多個類而產生的父類構造方法衝突

class Living {
  Living(){
    print("Runner");
  }
}

class Runner {

  run(){
    print("run");
  }
}

class Walker{
  walk(){
    print("run");
  }
}

class Person extends Living with Walker,Runner{
  Person(){
    print("Person");
  }
}

main(){
  Person toly = Person();
  toly.run();
  toly.walk();
}
複製程式碼

使用方法很簡單,在with關鍵字後面將類名,這是該類就是mixin類
mixin就相當於將其他類的能力混入到當前類,還是挺厲害的。
唯一的限制就是mixin類無法擁有建構函式,如果有構造方法會怎樣? 報錯唄。

Flutter之旅:Dart語法掃尾-包訪問-泛型--異常-非同步-mixin


6.3:關於mixin關鍵字

使用class關鍵字定義的類是可以當做mixin類使用的,比如上面的。
另外使用mixin關鍵字也可以來定義mixin類,如:

mixin Walker{
  walk(){
    print("run");
  }
}
複製程式碼

唯一的區別在於,你是否確定它是一個mixin類。
當你在mixin宣告的類中定義構造方法,會直接報錯。

Flutter之旅:Dart語法掃尾-包訪問-泛型--異常-非同步-mixin


6.4:關於混入的方法重名

取後混入的

class Runner {
  go(){
    print("Runner-go");
  }
}

mixin Walker{
  go(){
    print("Walker-go");
  }
}

class Person with Runner,Walker{
  Person(){
    print("Person");
  }
}

main(){
  Person toly = Person();
  toly.go();//Walker-go
}
複製程式碼

本文到此接近尾聲了,如果想快速嚐鮮Flutter,《Flutter七日》會是你的必備佳品;如果想細細探究它,那就跟隨我的腳步,完成一次Flutter之旅。
另外本人有一個Flutter微信交流群,歡迎小夥伴加入,共同探討Flutter的問題,本人微訊號:zdl1994328,期待與你的交流與切磋。

相關文章