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

        歡迎訪問 生活随笔!

        生活随笔

        當前位置: 首頁 > 运维知识 > windows >内容正文

        windows

        【Flutter】一文读懂混入类Mixin

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

        【Flutter】一文讀懂混入類Mixin

        基本介紹

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

        Mixin 是面向對象程序設計語言中的類,提供了方法的實現,其他類可以訪問 Mixin 類的方法而不必成為其子類;Mixin 為使用它的 Class 類提供額外的功能,但自身卻不單獨使用(不能單獨生成實例對象,屬于抽象類),Mixin 類通常作為功能模塊使用,在需要該功能時“混入”,而且不會使類的關系變得復雜;
        Mixin 有利于代碼復用性同時又避免了多繼承的復雜性,使用 Mixin 享有單一繼承的單純性和多重繼承的共有性,interface 接口與 Mixin 相同的地方是都可以多繼承,不同的地方在于 Mixin 是可以實現的;

        對應關系

        繼承 混入 接口
        關鍵字 extends with implements
        對應數量 1:1 1:n 1:n
        代碼設置順序
        耦合度

        舉例學習

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

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

        ,我們需要用多個對象表示一些 動物, 諸如 狗、鳥、魚、青蛙。其中

        1. 狗會跑
        2. 鳥會飛
        3. 魚會游泳
        4. 青蛙是兩棲動物,會跑,并且會游泳

        基于如下一些考慮

        • 動物特性可能會繼續增多,并且一個動物可能具備多種技能
        • 動物種類很多,但是可以歸大類。例如 鳥禽、哺乳類

        我們使用如下設計

        • 動物繼承自 Animal 抽象類
        • 跑、飛、游 抽象為接口

        我們按照上面的需求...讓copilotX幫我寫一個類的實現...

        可以看到AI生成的代碼還是很給力的,但是我們可以發現,Frog和Dog都實現了Run的抽象方法。

        假如我們現在嘗試讓代碼復用率變高,讓Run,Fly,Swim作為實現,看看會發生什么...

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

        原來這個寫法 Dart 會一直認為 super 調用是在調用一個 abstract 的函數,所以我們這時候需要把這里面集成的函數實現一一實現。

        這時候問題來了,Frog 和 Fish 都實現了 Swim 接口,這時候 swim 函數的內容我們需要重復的寫 2 遍!

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

        當然,作為一篇Mixin教學,我們對這個結果肯定是不滿意的...

        現在,我們完全沒學過類似Java的default關鍵字的知識點...我們只是個渴望dart的小白...

        選擇使用mixin,重新定義Run,Fly,Swim方法,子類也不再是實現接口而是混入。

        可以看到,mixin被混入到了類中,也實現了對應“抽象類”的特性。

        這里類的繼承關系我們可以梳理成下圖

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

        mixin,class,interface的異同

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

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

        所以,Dart中的任何一個class,既是類,又是接口,也可以當作mixin使用

        這意味著:

        • 混入類可以持有成員變量,也可以聲明和實現成員方法。而混入一個類,就可以訪問其中的成員屬性和方法,這點和繼承很像

        • 一個類可以混入若干個類,通過,分隔開,這個功能和接口類似,但是和接口不同的是:混入類本身可以對方法進行實現,而接口內必須是抽象方法

        • 混入類支持抽象方法,但是這要求了派生類必須實現抽象方法,這一點又和抽象類很像。

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

        mixin的限制

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

        所以...為了守護自然界的秩序,mixin提供了一種限制:on 關鍵字

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

        除此之外,on還可以限定mixin之間的繼承關系,參考下一小節

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

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

        因為剛剛提到,dart中的任何一個類都可以被混入,而使用mixin聲明的類,需要使用with關鍵字才可以替換。

        除此之外的一點小改動...

        細心的你可能會發現,在我們的樣例中直接這樣修改是沒辦法通過編譯的。這是因為上面那句話:

        mixin并不是對子類的拓展,而是對父類的拓展,也就是說,我們在代碼中,相當于將Animal拓展了一個Fly功能,而我們規定了,Fly方法只能被Bird及Bird的子類使用。Animal并不屬于Bird的子類(反倒是他的父類),所以會報錯。

        繼承的二義性問題

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

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

        在C++中,派生類繼承基類,對基類成員的訪問應該是確定的、唯一的,但是常常會有以下情況導致訪問不一致,產生二義性。

        1.在繼承時,基類之間、或基類與派生類之間發生成員同名時,將出現對成員訪問的不確定性——同名二義性。

        2.當派生類從多個基類派生,而這些基類又從同一個基類派生,則在訪問此共同基類中的成員時,將產生另一種不確定性——路徑二義性。

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

        混入類中,不能擁有構造方法,也就是說不能實例化。這一點跟抽象類接口是一樣的。

        看如下的實例:

        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();
        }
        
        

        運行代碼,得到如下的結果:

        MB

        MA

        我們可以得出結論:最后一個混入的mixin,會覆蓋前面的mixin的特性

        為了驗證這個結論,我們給mixin加入super調用和mixin的繼承關系

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

        運行代碼,得到如下結果:

        A

        MA

        MB

        A

        MB

        MA

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

        Mixin的線性化

        Dart 中的 mixin 通過創建一個類來實現,該類將 mixin 的實現層疊在一個超類之上以創建一個新類 ,它不是“在超類中”,而是在超類的“頂部”。

        我們可以得到以下幾個結論:

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

        最終我們可以得出一個很重要的結論

        聲明 mixin 的順序代表了繼承鏈的繼承順序,聲明在后面的 mixin,一般會最先執行

        線性化的覆蓋實例

        參考如下代碼

        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();
        
        

        按照我們常見的思維方式,可能會認為得到的結論為:

        A

        log MA

        MA

        MB

        但事實上,得到的輸出結果為:

        A

        log MB

        MA

        MB

        因為按照上面的工作原理,在mixin的繼承鏈建立了之后,最后聲明的mixin會把前面聲明的mixin函數覆蓋掉,所以即使我們此時在MA函數中調用了log,而事實上MA里面的log函數被MB覆蓋了,最后調用的是MB。

        小結論:調用了super就可以從前往后看執行順序,如果存在函數內同名調用函數的情況要從后往前看

        混入類之間的繼承關系

        另外,兩個混入類間可以通過 on 關鍵字產生類似于 繼承 的關系

        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內的成員變量。

        同時C with A,B不可以調換順序,否則編譯器會報錯。這也符合我們之前說的線性關系,因為“B繼承A”,所以,只有“B覆蓋了A”這種線性關系才是可以被接受的。

        extends,mixin,implements的執行順序

        class Ex{
          Ex(){
            print('extends constructor');
          }
          void show(){
            print('extends show');
          }
        }
        
        // dart 沒有 interface 關鍵字,但是可以使用 abstract class 來實現接口的功能
        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();
        }
        

        執行結果:

        extends constructor

        extends show

        mx1show

        mx2show

        it show

        extends constructor

        extends show

        mx2show

        mx1show

        it show

        結論:執行順序是 extends 繼承優先執行,之后是 with 混入,最后是 implements 接口重載;

        Flutter的runAPP

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

        WidgetsFlutterBinding.ensureInitialized 方法如下:

        WidgetsFlutterBinding 混合結構如下:

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

        BindingBase 及構造函數如下:

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

        從后到前依次執行其 initInstances 和 initServiceExtensions(如果有) 方法,由于 initInstances 和 initServiceExtensions 方法中首先執行 super.initInstances()super.initServiceExtensions() ,所以最后執行的順序為:BindingBase -> GestureBinding -> SchedulerBinding -> ServicesBinding -> PaintingBinding -> SemanticsBinding -> RendererBindinsg -> WidgetsBinding 。

        而在WidgetsBinding和RendererBinding中,都有一個叫做drawFrame的函數,而Widget的drawFrame調用了super.drawFrame,同時Widgets on Renderer

        這里反應的邏輯有如下兩點:

        • 保證widget等的drawFrame能夠先于render調用。保證了flutter在布局和渲染處理時 widgets->render
        • 保證了順序的同時,兩者仍然各個負責自己的部分

        參考文章

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

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

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

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

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

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

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

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

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

        總結

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

        如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。