40分鐘快速入門Dart基礎(中)

最近時間太忙,每天加班加點,可是咱們的學習的行爲不能落下。編程

小夥伴們繼續跟着我一塊兒來學習40分鐘快速入門Dart基礎(中)緩存

鎮樓目錄: bash

在本章咱們主要跟你們聊聊方法和類。這兩大章看似很少,其實也很多,不太小夥伴們不用擔憂,只要你們按照我下面的代碼敲一遍,保證能掌握大部分知識點!網絡

好廢話很少說,直接開幹。閉包

1、一等方法對象

Dart 是一個真正的面嚮對象語言,方法也是對象而且具備一種 類型 Function。 這意味着,方法能夠賦值給變量,也能夠當作其餘方法的參數,同時也能夠把方法當作參數調用另一個方法異步

import 'dart:core';

void main() {
  var list = ["黃藥師", "郭靖", "小龍女"];

  void printElement(element) {
    print(element);
  }
  list.forEach(printElement); 
}

複製代碼

在Java中若是須要可以通知調用者或者其餘地方方法執行過程的各類狀況,可能須要指定一個接口,其實就是咱們說的回調函數。好比View的onClickListener。而在Dart中,咱們能夠直接指定一個回調方法給調用的方法,由調用的方法在合適的時機執行這個回調。編程語言

void setListener(Function listener){
    listener("Success");
}
//或者
void setListener(void listener(String result)){
    listener("Success");
}

//兩種方式,第一種調用者根本不肯定 回調函數的返回值、參數是些什麼
//第二中則須要寫這麼一大段 太麻煩了。

//第三種:類型定義 將返回值爲voide,參數爲一個String的方法定義爲一個類型。
typedef  void Listener(String result);
void setListener(Listener listener){
  listener("Success");
}

複製代碼

在Dart 中方法能夠有兩種類型的參數:必需的和可選的。 必需的參數須要在參數列表前面, 後面再定義可選參數。ide

2、可選命名參數

什麼叫可選命名函數,其實說白了就是把方法的參數放到 {} 中就變成了可選命名參數。函數

import 'dart:core';

void main() {
  int add({int i, int j}) {
    if (i == null || j == null) {
      return 0;
    }
    return i + j;
  }

  //全參調用
  print("--1--${add(i: 10, j: 20)}"); //輸出:30
  //不傳參數(調用也是正確的)
  print("--2--${add()}"); //輸出:0
  //選擇傳遞參數
  print("--3--${add(j: 20)}"); //輸出:0
  //與位置無關
  print("--4--${add(j: 20, i: 10)}"); //輸出:30
}

複製代碼

3、可選位置參數

什麼叫可選命名函數,其實說白了就是把方法的參數放到 [] 中就變成了可選命名參數。工具

void main() {
  int add([int i, int j]) {
    if (i == null || j == null) {
      return 0;
    }
    return i + j;
  }

  //全參調用
  print("--1--${add(10, 20)}"); //輸出:30
  //不傳參數(調用也是正確的)
  print("--2--${add()}"); //輸出:0
  //選擇傳遞參數
  print("--3--${add(10)}"); //輸出:0
}

複製代碼

4、默認參數

什麼叫作默認參數,其實就是在定義方法的時候,可選參數可使用 = 來定義可選參數的默認值。

import 'dart:core';

void main() {
  int sum([int age1 = 1, int age2 = 2])  {
    if (age1 == null || age2 == null) {
      return 0;
    }
    return age1 + age2;
  }

  //全參調用
  print("--1--${sum(10, 20)}"); //輸出:30
  //不傳參數(調用也是正確的)
  print("--2--${sum()}"); //輸出:3
  //選擇傳遞參數
  print("--3--${sum(20)}"); //輸出:22
}
複製代碼

5、匿名函數

首先什麼叫匿名函數,簡單來講:

大多數方法都是有名字的,好比 main() 或 printElement()。你能夠建立一個沒有名字的方法,稱之爲 匿名函數,或Lambda表達式 或Closure閉包。你能夠將匿名方法賦值給一個變量而後使用它,好比將該變量添加到集合或從中刪除。

([Type] param1, …) { 
  codeBlock; 
}; 
複製代碼

匿名方法看起來與命名方法相似,在括號之間能夠定義參數,參數之間用逗號分割。 後面大括號中的內容則爲函數體:下面代碼定義了只有一個參數 item 且沒有參數類型的匿名方法。List 中的每一個元素都會調用這個函數,打印元素位置和值的字符串:

import 'dart:core';

void main() {
  var list = ['黃藥師', '楊過', '老頑童'];
  list.forEach((item) {
    print('${list.indexOf(item)}: $item'); //輸出:0: 黃藥師 1: 楊過 2: 老頑童
  });

  // 若是函數體內只有一行語句,你可使用箭頭語法:
  list.forEach(
          (item) => print('${list.indexOf(item)}: $item')); //輸出:0: 黃藥師 1: 楊過 2: 老頑童
}
複製代碼

至此在開發flutter 過程當中經常使用的方法知識點咱們講完了啊

其實方法內容不止這些,接下來的小夥伴能夠自行挖掘:好比方法做用域等。

接下來咱們進入類講解

6、類

Dart 是一個面向對象編程語言。 每一個對象都是一個類的實例,全部的類都繼承於 Object。同時也支持面向對象的特性,好比:類、接口、抽象等。

使用class關鍵字聲明一個dart類,後面跟類名,而且由一對花括號包圍的類體 全部類都有同一個基類,Object,dart的繼承機制使用了Mixin;

//僞代碼
class class_name {
  <fields> //字段,類中聲明任何變量、常量;
  <getters/setters>  // 若是對象爲final,或const,只有一個getter方法 這個等會咱們會有實例產生
  <constructors>   // 構造函數,爲類的對象分配內存
  <functions>   //函數,也叫方法,對象的操做;
}
複製代碼

上面咱們提到 <getters/setters>方法,其實每一個實例變量都會自動生成一個 getter 方法(隱含的)。 非final 實例變量還會自動生成一個 setter 方法。

class User {
  var name;
  var age;

  User(this.name, this.age);
}


void main(){
  
  var user =User("黃藥師",50);

  var _name = user.name;
  var _age = user.age;
  
  print("-----$_name$_age"); //輸出:黃藥師 50
  
}
複製代碼

7、類--構造函數

Dart構造函數有種實現方式:

  • 默認構造方法
  • 命名構造方法Class.name(var param)
  • 調用父類構造方法
  • 不可變對象,定義編譯時常量對象,構造函數前加const
  • 工廠構造函數:factory

默認構造函數每每也是咱們最多見的也是最簡單的以下

class User {
  var name;
  var age;

  User(this.name, this.age); //默認構造函數
}
複製代碼

命名構造函數

Dart 並不支持構造函數的重載,而採用了命名構造函數爲一個類實現多個構造函數:

class User {
  var name;
  var age;

  User(this.name, this.age); //默認構造函數
  //User(this.name); ///錯誤,由於不許許重載
  User.age(this.age) {
    name = "歐陽鋒";
  }
}

void main() {
  var user = User.age(50);
  print("----${user.name}${user.age}"); //輸出:歐陽鋒 50
}
複製代碼

重定向構造函數

有時候一個構造函數會調動類中的其餘構造函數(在Java中就是 this(...))。 一個重定向構造函數是沒有代碼的,在構造函數聲明後,使用 冒號調用其餘構造函數。

class User {
  var name;
  var age;

  User(this.name, this.age); //默認構造函數

  User.user(name, age) : this(name, age);
}

void main() {
  var user = User.user("黃藥師", 50);
  print("----${user.name}${user.age}"); //輸出:黃藥師50
}
複製代碼

常量構造函數

若是你的類提供一個狀態不變的對象,你能夠把這些對象 定義爲編譯時常量。要實現這個功能,須要定義一個 const 構造函數, 而且聲明全部類的變量爲 final。

class User {
  final String name;
  final int age;

  const User(this.name, this.age); //默認構造函數

}

void main() {
  var user = User("黃藥師", 50);
  print("----${user.name}${user.age}"); //輸出:黃藥師50
}
複製代碼

工廠構造函數

當實現一個使用factory 關鍵詞修飾的構造函數時,這個構造函數沒必要建立類的新實例。例如,一個工廠構造函數 可能從緩存中獲取一個實例並返回,或者 返回一個子類型的實例。(工廠構造函數沒法訪問 this)

//工廠構造方法   若是一個構造方法並不老是返回一個新的對象,這個時候可使用factory來定義這個構造方法。
class Logger {
  final String name;
  bool mute = false;

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

  factory Logger(String name) {
    if (_cache.containsKey(name)) {
      return _cache[name];
    } else {
      final logger = new Logger._internal(name);
      _cache[name] = logger;
      return logger;
    }
  }

  Logger._internal(this.name);

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

//調用
void main() {
  //工廠
  var logger = new Logger('UI');
  logger.log('Button clicked');
}
複製代碼

要注意的是工廠構造方法時無法訪問this關鍵字的,因此上面就有了在類的內部這麼調用構造方法的代碼:final logger = new Logger._internal(name); 在上面工廠構造方法中,若是緩存中存在傳入的name的key值,則取出緩存中的對應value返回。 若是緩存中沒找到,就會經過命名構造方法來新建一個對象,緩存起來後返回

補充說明:藉助工廠構造函數可以實現單例:(實用場景:flutter 網絡請求Dio應用)

//使用工廠構造實現單例
class DioUtil {
  static final DioUtil _instance = DioUtil._init();
  static Dio _dio;

  factory DioUtil() {
    return _instance;
  }

  DioUtil._init() {
    _dio = new Dio();
  }
}
複製代碼

8、方法

上面是方法:方法是對象提供行爲的函數。

Getters 和 Setters

Dart中每一個實例變量都隱含的具備一個 getter, 若是變量不是 final 的則還有一個 setter。能夠經過實現 getter 和 setter 來建立新的屬性, 使用 get 和 set 關鍵字定義 getter 和 setter:

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

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

  //使用 get定義了一個 right 屬性
  num get right             => left + width;
  set right(num value)  => left = value - width;
}

void main() {
  var rect = Rect(0, 0, 10, 10);
  print(rect.right); //10
  rect.right = 15;
  print(rect.left);  //5
}
複製代碼

使用 Getter 和 Setter 的好處是,你能夠先使用你的實例變量,過一段時間過再將它們包裹成方法且不須要改動任何代碼,即先定義後更改且不影響原有邏輯。

9、抽象類、抽象方法、還有繼承

實例方法、Getter 方法以及 Setter 方法均可以是抽象的,定義一個接口方法而不去作具體的實現讓實現它的類去實現該方法,抽象方法只能存在於抽象類中,抽象類的定義跟Java的抽象類相似,就不單獨介紹了。

abstract class User {
  void say(); //定義一個抽象方法
}
class Person extends User{
  @override
  void say() {
    // 提供一個實現,因此在這裏該方法再也不是抽象的……
  }

}
複製代碼

說明:抽象類不能被實例化,除非定義工廠方法並返回子類。

abstract class User {
  String name;
  //默認構造方法
  User(this.name);
  //工廠方法返回Child實例
  factory User.test(String name){
    return new Child(name);
  }
  void printName();
}
// extends 繼承抽象類
class Child extends User{
  Child(String name) : super(name);

  @override
  void printName() {
    print(name);
  }
}

void main() {
  var p = User.test("黃藥師");
  print(p.runtimeType); //輸出實際類型 Child
  p.printName();//輸出實際類型 黃藥師
}
複製代碼

10、接口

Dart 沒有像 Java 用單獨的關鍵字 interface 來定義接口,普通用 class 聲明的類就能夠是接口,能夠經過關鍵字 implements來實現一個或多個接口並實現每一個接口定義的 API:

// Person 類的隱式接口中包含 greet() 方法。
class User {
  // _name 變量一樣包含在接口中,但它只是庫內可見的。
  final _name;

  // 構造函數不在接口中。
  User(this._name);

  // greet() 方法在接口中。
  String greet(String who) => '你好,$who。我是$_name。';
}

// Person 接口的一個實現。
class Impostor implements User {
  get _name => '';

  String greet(String who) => '你好$who。你知道我是誰嗎?';
}

String greetBob(User person) => person.greet('黃藥師');

void main() {
  print(greetBob(User('歐陽鋒'))); //輸出:你好,黃藥師。我是歐陽鋒。
  print(greetBob(Impostor())); //輸出:你好黃藥師。你知道我是誰嗎?
}
複製代碼

這時疑問來了,接口跟繼承有什麼區別,不就是多繼承嗎? 接口的實現則意味着,子類獲取到的僅僅是接口的成員變量符號和方法符號,須要從新實現成員變量,以及方法的聲明和初始化,不然編譯器會報錯。而繼承能夠選擇不從新實現,這是最大的區別。

可能小夥伴以爲有點繞:那咱們總結一下。其實就兩句話:

  • 單繼承,多實現。
  • 繼承能夠有選擇的重寫父類方法而且可使用super,實現強制從新定義接口全部成員。

11、可調用的類:

若是 Dart 類實現了 call() 函數則 能夠當作方法來調用。

class User {
  call(String name, int age) => '$name $age!';
}

main() {
  var c = new User();
  var out = c("黃藥師",50);
  print(out); //輸出:黃藥師 50!
}
複製代碼

12、混合Mixins

在面向對象的世界中,咱們最熟悉的莫過於class、 abstract class和interface。Dart做爲一門現代面向對象編程語音,在原有的特性基礎上,新增了一些新的特性如:Mixins

什麼是Mixins:

簡單的理解,就是用來複用多個類之間的代碼,減小耦合。咱們直接來看一個例子。

咱們在沒有使用Mixins的從前:

假設,咱們如今正在開發一個動物大全App,咱們須要建立一個Duck類。做爲一個有豐富面向對象編程經驗的開發者,你天然的將全部和Duck有類似特徵的抽取成一個abstract class。

/// Bird
abstract class Bird {
    void shout() {
        println('shouting');
    }
}

/// WaterborneBird
abstract class WaterborneBird extends Bird {
    void swim() {
        println('swimming');
    }
}

/// Duck
class Duck extends WaterborneBird {
    void doDuckThings() {
        shout();
        swim();
        println('quack quack quack!')
    }
}
複製代碼

很好,咱們清楚的將鴨子納入水中生活的鳥類,加入其它的鳥類也變得很是容易。可是,如今咱們須要加入金魚了,因而咱們和上面同樣編寫代碼。

/// Fish
abstract class Fish {
    void swim() {
        println("swimming")
    }
}

/// GoldFish
class GoldFish extends Fish {
    void doGoldFishThings() {
        swim();
        pringln('zzz...');
    }
}
複製代碼

這是咱們發現金魚和鴨子同樣擁有swim的特性,在這個例子中是很是簡單的,可是若是咱們有複雜的行爲須要賦予給一個新的類,咱們就要大量編寫重複的代碼了。

使用Mixins

咱們聲明一個Swimming的mixin

mixin Swimming {
    void swim() {
        println('swimming')
    }
}
複製代碼

咱們可使用with關鍵字將mixin加入到class中,其實看到這裏你可能已經回想到咱們其實可能已經用過這個with關鍵字了。接下來,咱們就能夠對上面的代碼進行改造了:

/// Bird
abstract class Bird {
    void shout() {
        println('游泳');
    }
}


/// Duck
class Duck extends Bird with Swimming {
    void doDuckThings() {
        shout();
        swim();
        println('跳躍!')
    }
}
複製代碼
/// Fish
abstract class Fish {

}

/// GoldFish
class GoldFish extends Fish with Swimming {
    void doGoldFishThings() {
        swim();
        pringln('zzz...');
    }
}
複製代碼

mixins彌補了接口和繼承的不足,繼承只能單繼承,而接口沒法複用實現,mixins卻能夠多混入而且能利用到混入類。

咱們在來看一個例子作比較:

abstract class Swimming{
  void swimming(){
    print("游泳");
  }
}

abstract class Jump{
  void jump(){
    print("跳躍");
  }
}

//只能單繼承,若是須要Jump,只能以implements的形式
class HuangYaoShi extends Swimming implements Jump{
  //實現接口
  void jump(){
    print("跳躍");
  }
}

//可是實際上,咱們常常不須要從新實現Jump方法,複用Jump所實現的jump方法就能夠了
//這時使用混合可以更加方便
class HuangYaoShi with Swimming, Jump {}
複製代碼

關於Mixins,還有不少須要注意的事情,咱們雖然可使用Mixins對代碼進行一些簡化,可是要創建在對需求和類之間的關係準確理解的基礎上。建議多去看看Flutter中使用Mixins實現的一些源碼,從裏面吸收一些正確的經驗。

下一章也是咱們Dart最後一章咱們繼續講解Dart:異步、泛型、異常

參考:上面動物例子轉載了該博主的文章: 深刻理解Dart之Mixins

最後附上:本人本身收集的工具庫(待完成) 工具庫-待完成

知乎: 如何快速掌握Dart這門語言並進階Flutter變成大神

相關文章
相關標籤/搜索