日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > windows >内容正文

windows

【Flutter】一文读懂混入类Mixin

發(fā)布時(shí)間:2023/11/16 windows 35 coder
生活随笔 收集整理的這篇文章主要介紹了 【Flutter】一文读懂混入类Mixin 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

【Flutter】一文讀懂混入類Mixin

基本介紹

Mixin是一種有利于代碼復(fù)用,又避免了多繼承的解決方案。

Mixin 是面向?qū)ο蟪绦蛟O(shè)計(jì)語言中的類,提供了方法的實(shí)現(xiàn),其他類可以訪問 Mixin 類的方法而不必成為其子類;Mixin 為使用它的 Class 類提供額外的功能,但自身卻不單獨(dú)使用(不能單獨(dú)生成實(shí)例對(duì)象,屬于抽象類),Mixin 類通常作為功能模塊使用,在需要該功能時(shí)“混入”,而且不會(huì)使類的關(guān)系變得復(fù)雜;
Mixin 有利于代碼復(fù)用性同時(shí)又避免了多繼承的復(fù)雜性,使用 Mixin 享有單一繼承的單純性和多重繼承的共有性,interface 接口與 Mixin 相同的地方是都可以多繼承,不同的地方在于 Mixin 是可以實(shí)現(xiàn)的;

對(duì)應(yīng)關(guān)系

繼承 混入 接口
關(guān)鍵字 extends with implements
對(duì)應(yīng)數(shù)量 1:1 1:n 1:n
代碼設(shè)置順序
耦合度

舉例學(xué)習(xí)

首先,眾所周知...Java只能單繼承,

假如我們面臨下面這一種需求:

,我們需要用多個(gè)對(duì)象表示一些 動(dòng)物, 諸如 狗、鳥、魚、青蛙。其中

  1. 狗會(huì)跑
  2. 鳥會(huì)飛
  3. 魚會(huì)游泳
  4. 青蛙是兩棲動(dòng)物,會(huì)跑,并且會(huì)游泳

基于如下一些考慮

  • 動(dòng)物特性可能會(huì)繼續(xù)增多,并且一個(gè)動(dòng)物可能具備多種技能
  • 動(dòng)物種類很多,但是可以歸大類。例如 鳥禽、哺乳類

我們使用如下設(shè)計(jì)

  • 動(dòng)物繼承自 Animal 抽象類
  • 跑、飛、游 抽象為接口

我們按照上面的需求...讓copilotX幫我寫一個(gè)類的實(shí)現(xiàn)...

可以看到AI生成的代碼還是很給力的,但是我們可以發(fā)現(xiàn),F(xiàn)rog和Dog都實(shí)現(xiàn)了Run的抽象方法。

假如我們現(xiàn)在嘗試讓代碼復(fù)用率變高,讓Run,F(xiàn)ly,Swim作為實(shí)現(xiàn),看看會(huì)發(fā)生什么...

可以看到,我們的Copilit告訴了我們問題

原來這個(gè)寫法 Dart 會(huì)一直認(rèn)為 super 調(diào)用是在調(diào)用一個(gè) abstract 的函數(shù),所以我們這時(shí)候需要把這里面集成的函數(shù)實(shí)現(xiàn)一一實(shí)現(xiàn)。

這時(shí)候問題來了,F(xiàn)rog 和 Fish 都實(shí)現(xiàn)了 Swim 接口,這時(shí)候 swim 函數(shù)的內(nèi)容我們需要重復(fù)的寫 2 遍!

(當(dāng)然我們指的就是前面AI生成的代碼)

當(dāng)然,作為一篇Mixin教學(xué),我們對(duì)這個(gè)結(jié)果肯定是不滿意的...

現(xiàn)在,我們完全沒學(xué)過類似Java的default關(guān)鍵字的知識(shí)點(diǎn)...我們只是個(gè)渴望dart的小白...

選擇使用mixin,重新定義Run,F(xiàn)ly,Swim方法,子類也不再是實(shí)現(xiàn)接口而是混入。

可以看到,mixin被混入到了類中,也實(shí)現(xiàn)了對(duì)應(yīng)“抽象類”的特性。

這里類的繼承關(guān)系我們可以梳理成下圖

這里也可以增加一個(gè)新的理解:mixin并不是對(duì)子類的拓展,而是對(duì)父類的拓展

mixin,class,interface的異同

mixin也可以使用class關(guān)鍵字定義,也可以當(dāng)做普通class一樣使用。
mixin可以使用with定義,這樣定義的mixin就只能通過with關(guān)鍵字引用了。

Dart是沒有interface這種東西的,但并不意味著這門語言沒有接口,事實(shí)上,Dart任何一個(gè)類都是接口,你可以實(shí)現(xiàn)任何一個(gè)類,只需要重寫那個(gè)類里面的所有具體方法。

所以,Dart中的任何一個(gè)class,既是類,又是接口,也可以當(dāng)作mixin使用

這意味著:

  • 混入類可以持有成員變量,也可以聲明和實(shí)現(xiàn)成員方法。而混入一個(gè)類,就可以訪問其中的成員屬性和方法,這點(diǎn)和繼承很像

  • 一個(gè)類可以混入若干個(gè)類,通過,分隔開,這個(gè)功能和接口類似,但是和接口不同的是:混入類本身可以對(duì)方法進(jìn)行實(shí)現(xiàn),而接口內(nèi)必須是抽象方法

  • 混入類支持抽象方法,但是這要求了派生類必須實(shí)現(xiàn)抽象方法,這一點(diǎn)又和抽象類很像。

    mixin PaintAble{
     ?late Paint painter;
     ?void paint(){
     ? ?print("=====$runtimeType paint====");
      }
     ?void init();
    }
    
    class Shape with MoveAble,PaintAble{
     ?@override
     ?void init() {
     ? ?painter = Paint();
      }
    }
    // 這里的Shape作為派生類,必須實(shí)現(xiàn)PaintAble中聲明的抽象方法init
    

mixin的限制

可以看到,在混入了之后,就可以使用mixin的所有方法了,但是有時(shí)我們并不希望所有類都可以使用一些方法。比如我在Dog類中with一個(gè)Fly,這就意味著我們的狗可以飛了!

所以...為了守護(hù)自然界的秩序,mixin提供了一種限制:on 關(guān)鍵字

規(guī)定了:on后面銜接的類和它的子類才可以被混入

除此之外,on還可以限定mixin之間的繼承關(guān)系,參考下一小節(jié)

mixin Fly on Bird{
    void fly(){
        print('只有鳥類可以混入Fly')
    }
}

除了類的限制外,mixin本身就是一種限制。

因?yàn)閯倓偺岬剑琩art中的任何一個(gè)類都可以被混入,而使用mixin聲明的類,需要使用with關(guān)鍵字才可以替換。

除此之外的一點(diǎn)小改動(dòng)...

細(xì)心的你可能會(huì)發(fā)現(xiàn),在我們的樣例中直接這樣修改是沒辦法通過編譯的。這是因?yàn)樯厦婺蔷湓挘?/p>

mixin并不是對(duì)子類的拓展,而是對(duì)父類的拓展,也就是說,我們?cè)诖a中,相當(dāng)于將Animal拓展了一個(gè)Fly功能,而我們規(guī)定了,F(xiàn)ly方法只能被Bird及Bird的子類使用。Animal并不屬于Bird的子類(反倒是他的父類),所以會(huì)報(bào)錯(cuò)。

繼承的二義性問題

先說說什么是二義性問題:

(內(nèi)容參考如下文章:C++多繼承中的二義性問題_繼承的二義性-CSDN博客)

在C++中,派生類繼承基類,對(duì)基類成員的訪問應(yīng)該是確定的、唯一的,但是常常會(huì)有以下情況導(dǎo)致訪問不一致,產(chǎn)生二義性。

1.在繼承時(shí),基類之間、或基類與派生類之間發(fā)生成員同名時(shí),將出現(xiàn)對(duì)成員訪問的不確定性——同名二義性。

2.當(dāng)派生類從多個(gè)基類派生,而這些基類又從同一個(gè)基類派生,則在訪問此共同基類中的成員時(shí),將產(chǎn)生另一種不確定性——路徑二義性。

而在接口中,犧牲了接口的普通成員方法實(shí)現(xiàn),最終才解決二義性問題,最終能夠支持多實(shí)現(xiàn)。

混入類中,不能擁有構(gòu)造方法,也就是說不能實(shí)例化。這一點(diǎn)跟抽象類接口是一樣的。

看如下的實(shí)例:

class S {
  fun() => print('A');
}

mixin MA {
  fun() => print('MA');
}
mixin MB {
  fun() => print('MB');
}

class A extends S with MA, MB {}

class B extends S with MB, MA {}

main() {
  A a = A();
  a.fun();
  B b = B();
  b.fun();
}

運(yùn)行代碼,得到如下的結(jié)果:

MB

MA

我們可以得出結(jié)論:最后一個(gè)混入的mixin,會(huì)覆蓋前面的mixin的特性

為了驗(yàn)證這個(gè)結(jié)論,我們給mixin加入super調(diào)用和mixin的繼承關(guān)系

mixin MA on S {
  fun() {
    super.fun();
    print('MA');
  }
}
mixin MB on S {
  fun() {
    super.fun();
    print('MB');
  }
}

運(yùn)行代碼,得到如下結(jié)果:

A

MA

MB

A

MB

MA

這里我們得到mixin的工作方式:線性化

Mixin的線性化

Dart 中的 mixin 通過創(chuàng)建一個(gè)類來實(shí)現(xiàn),該類將 mixin 的實(shí)現(xiàn)層疊在一個(gè)超類之上以創(chuàng)建一個(gè)新類 ,它不是“在超類中”,而是在超類的“頂部”。

我們可以得到以下幾個(gè)結(jié)論:

  1. mixin 可以實(shí)現(xiàn)類似多重繼承的功能,但是實(shí)際上和多重繼承又不一樣。多重繼承中相同的函數(shù)執(zhí)行并不會(huì)存在 ”父子“ 關(guān)系
  2. mixin 可以抽象和重用一系列特性
  3. mixin 實(shí)際上實(shí)現(xiàn)了一條繼承鏈
  4. A is S,A is MA,A is MB。

最終我們可以得出一個(gè)很重要的結(jié)論

聲明 mixin 的順序代表了繼承鏈的繼承順序,聲明在后面的 mixin,一般會(huì)最先執(zhí)行

線性化的覆蓋實(shí)例

參考如下代碼

class S {
  fun()=>print('A');
}
mixin MA on S {
  fun() {
    super.fun();
    log();
    print('MA');
  }

  log() {
    print('log MA');
  }
}
mixin MB on S {
  fun() {
    super.fun();
    print('MB');
  }

  log() {
    print('log MB');
  }
}

class A extends S with MA,MB {}
A a = A();
a.fun();

按照我們常見的思維方式,可能會(huì)認(rèn)為得到的結(jié)論為:

A

log MA

MA

MB

但事實(shí)上,得到的輸出結(jié)果為:

A

log MB

MA

MB

因?yàn)榘凑丈厦娴墓ぷ髟恚?code>mixin的繼承鏈建立了之后,最后聲明的mixin會(huì)把前面聲明的mixin函數(shù)覆蓋掉,所以即使我們此時(shí)在MA函數(shù)中調(diào)用了log,而事實(shí)上MA里面的log函數(shù)被MB覆蓋了,最后調(diào)用的是MB。

小結(jié)論:調(diào)用了super就可以從前往后看執(zhí)行順序,如果存在函數(shù)內(nèi)同名調(diào)用函數(shù)的情況要從后往前看

混入類之間的繼承關(guān)系

另外,兩個(gè)混入類間可以通過 on 關(guān)鍵字產(chǎn)生類似于 繼承 的關(guān)系

mixin A{
    int i = 5;
}
mixin B on A{
    int j = 6;
    void show(){
        print(i);
        print(j);
    }
}
class C with A,B{

}
main(){
    C c = new C();
    c.show();
}

可以看到,B中可以通過on A來訪問A內(nèi)的成員變量。

同時(shí)C with A,B不可以調(diào)換順序,否則編譯器會(huì)報(bào)錯(cuò)。這也符合我們之前說的線性關(guān)系,因?yàn)椤癇繼承A”,所以,只有“B覆蓋了A”這種線性關(guān)系才是可以被接受的。

extends,mixin,implements的執(zhí)行順序

class Ex{
  Ex(){
    print('extends constructor');
  }
  void show(){
    print('extends show');
  }
}

// dart 沒有 interface 關(guān)鍵字,但是可以使用 abstract class 來實(shí)現(xiàn)接口的功能
abstract class It{
  void show();
}


mixin mx1 on Ex{
  void show(){
    super.show();
    print('mx1show');
  }
}

mixin mx2 on Ex{
  void show(){
    super.show();
    print('mx2show');
  }
}

class C12 extends Ex with mx1,mx2 implements It{
  @override
  void show() {
    super.show();
    print('it show');
  }
}

class C21 extends Ex with mx2,mx1 implements It{
  @override
  void show() {
    super.show();
    print('it show');
  }
}

void main(){
  C12 c12 = new C12();
  c12.show();
  C21 c21 = new C21();
  c21.show();
}

執(zhí)行結(jié)果:

extends constructor

extends show

mx1show

mx2show

it show

extends constructor

extends show

mx2show

mx1show

it show

結(jié)論:執(zhí)行順序是 extends 繼承優(yōu)先執(zhí)行,之后是 with 混入,最后是 implements 接口重載;

Flutter的runAPP

接下來我們回到Flutter,看一下runAPP()的形式

WidgetsFlutterBinding.ensureInitialized 方法如下:

WidgetsFlutterBinding 混合結(jié)構(gòu)如下:

class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {

BindingBase 及構(gòu)造函數(shù)如下:

其執(zhí)行了 initInstances 和 initServiceExtensions 方法。看下面混合的順序:

從后到前依次執(zhí)行其 initInstances 和 initServiceExtensions(如果有) 方法,由于 initInstances 和 initServiceExtensions 方法中首先執(zhí)行 super.initInstances()super.initServiceExtensions() ,所以最后執(zhí)行的順序?yàn)椋築indingBase -> GestureBinding -> SchedulerBinding -> ServicesBinding -> PaintingBinding -> SemanticsBinding -> RendererBindinsg -> WidgetsBinding 。

而在WidgetsBinding和RendererBinding中,都有一個(gè)叫做drawFrame的函數(shù),而Widget的drawFrame調(diào)用了super.drawFrame,同時(shí)Widgets on Renderer

這里反應(yīng)的邏輯有如下兩點(diǎn):

  • 保證widget等的drawFrame能夠先于render調(diào)用。保證了flutter在布局和渲染處理時(shí) widgets->render
  • 保證了順序的同時(shí),兩者仍然各個(gè)負(fù)責(zé)自己的部分

參考文章

Flutter 語法進(jìn)階 | 深入理解混入類 mixin - 掘金 (juejin.cn)

徹底理解 Dart mixin 機(jī)制 - 掘金 (juejin.cn)

Flutter 必知必會(huì)系列 —— mixin 和 BindingBase 的巧妙配合 - 掘金 (juejin.cn)

【Flutter 專題】103 初識(shí) Flutter Mixin - 掘金 (juejin.cn)

跟我學(xué)flutter:我們來舉個(gè)例子通俗易懂講解dart 中的 mixin - 掘金 (juejin.cn)

Flutter 中不得不會(huì)的 mixin - 老孟Flutter - 博客園 (cnblogs.com)

深入理解 Dart mixin 機(jī)制 - 知乎 (zhihu.com)

C++多繼承中的二義性問題_繼承的二義性-CSDN博客

Flutter 必知必會(huì)系列 —— runApp 做了啥 - 掘金 (juejin.cn)

總結(jié)

以上是生活随笔為你收集整理的【Flutter】一文读懂混入类Mixin的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。