美文网首页flutterFlutterFlutter
Flutter 知识梳理 (Dart) - implements

Flutter 知识梳理 (Dart) - implements

作者: 泽毛 | 来源:发表于2019-07-09 16:26 被阅读12次

    一、前言

    在使用Java语言设计类之间关系的时候,我们会接触到 组成单元关系连接 这两类概念:

    • 组成单元:普通类、abstract抽象类,interface接口。
    • 关系连接:implements实现,extends继承。

    而在Dart当中,对于这两类概念进行了增减:

    • 组成单元:普通类,abstract抽象类、mixin
    • 关系连接:implements实现、extends继承、with混入。

    最大的不同有两点:

    • 去掉了interface
    • 增加了混入的概念。

    下面我们就来看一下其中涉及到的知识点,前面两节对比一下JavaDart的区别,最后着重介绍混入的概念。

    推荐给大家一个网站:https://dartpad.dartlang.org/ 可以在线运行。

    二、组成单元

    2.1 普通类

    JavaDart的普通类有区别,但是不影响我们设计类之间的关系,因此不再赘述。

    2.2 abstract 抽象类

    JavaDart的抽象类定义时大体是一样的,我们可以在其中定义变量、普通方法、抽象方法,它和普通类最大的区别就是 抽象类不能实例化

    JavaDart在使用抽象类时有一点不同:Dart在定义抽象方法时,不需要用abstract修饰

    abstract class DartAbs {
      
      void absMethod();
      
    }
    

    2.3 interface 接口

    Dart中,没有 interface 关键字

    顺带我们复习一下Javaabstractinterface的一些要点:

    • 抽象类和接口都不能被实例化。
    • 抽象类要被子类继承,接口要被类实现。
    • 接口只能做方法的声明,抽象类可以做方法的声明,也可以做方法的实现。
    • 接口里定义的变量只能是公共的静态常量,抽象类中的变量可以是普通变量。
    • 抽象类里的抽象方法必须全部被子类实现;接口的接口方法必须全部被子类实现,否则只能为抽象类。
    • 抽象类里可以没有抽象方法。
    • 如果一个类里有抽象方法,那么这个类只能是抽象类。
    • 抽象方法要被实现,所以不能是静态的,也不能是私有的。
    • 接口可继承接口,并可多继承接口,但类只能单继承。

    三、关系连接

    3.1 extends

    JavaDartextends是一致的,只可以单继承,要注意的点是:

    • 子类可以继承父类里面 可见的属性和方法
      • 对于Java来说,可见指的是非private修饰,
      • Dart来说,指的是非下划线_开头。
    • 子类调用父类的方法,使用super关键字。
    • 子类不会 继承 父类的构造函数。
    class Extends {
      
      void base() {
        print('base');
      }
      
      void log() {
        print('extends');
      }
      
    }
    
    class Log extends Extends {
      
      log() {
        print('log');
      }
      
    }
    
    void main() {
      Log().base();
      Log().log();
    }
    

    输出结果:

    base
    log
    

    3.2 implements

    implementsextends最大的不同就是允许后面接上多个普通或者抽象类,当我们使用B implement A修饰时,那么A中的所有的属性和方法都要在A中实现,无论它原来是抽象方法还是普通方法

    也就是说如果我们只想要A中的接口定义,而不想要它的实现,那么就试用implements

    class Implements {
      
      void base() {
        print('base');
      }
        
      void log() {
        print('extends');
      }
      
    }
    
    class Log implements Implements {
      
      base() {
        print('log#base');
      }
      
      log() {
        print('log');
      }
      
    }
    
    void main() {
      Log().base();
      Log().log();
    }
    

    输出结果:

    log#base
    log
    

    四、混入

    前面我们介绍的都是Java中接触过的概念,下面我们来介绍Dart中特有的概念 - 混入。

    4.1 mixin

    mixin用于修饰类,和abstract类似,该类可以拥有成员变量、普通方法、抽象方法,但是不可以实例化。mixin一般用于描述一种具有某种功能的组块,而某一对象可以拥有多个不同功能的组块。

    (1) 最简单

    最简单的mixinmixin & with关键字组成。

    举个例子,我们有一种能力是 '绘画',而拥有这种能力的是 ‘教师’,那么实现如下:

    mixin DrawFunc {
      
      String content = '..';
      
      String what();
        
      void draw() {
        print('I can draw ${what()}');  
      }
      
    }
    
    class Teacher with DrawFunc {
      
      String what() => "car";
      
    }
    
    void main() {
      Teacher().draw();
    }
    

    (2) 限定类型

    我们限定了 '绘画' 这种能力只能够用在 '人类' 上面,示例如下:

    class Person {}
    
    mixin DrawFunc on Person {
      
      String content = '..';
      
      String what();
        
      void draw() {
        print('I can draw ${what()}');  
      }
      
    }
    
    class Teacher extends Person with DrawFunc {
      
      String what() => "car";
      
    }
    
    void main() {
      Teacher().draw();
    }
    

    当我们在mixin上使用了on关键字,那么mixin只能在那个类的子类上使用,而mixin可以调用那个类的方法。

    (3) 多个类型

    在 '绘画' 的基础上,我们增加一种新的能力 '唱歌',示例如下:

    class Person {}
    
    mixin DrawFunc on Person {
      
      String content = '..';
      
      String what();
        
      void draw() {
        print('I can draw ${what()}');  
      }
      
    }
    
    mixin SingFunc on Person {
      
      void sing() {
        print('I can sing');
      }
    }
    
    class Teacher extends Person with DrawFunc, SingFunc {
      
      String what() => "car";
      
    }
    
    void main() {
      Teacher().draw();
      Teacher().sing();
    }
    

    (4) on 的一种复杂变形

    关于on还有一种复杂的变形,我们在 '唱歌' 上增加一条约束,要求它必须是在DrawFunc之上:

    mixin SingFunc on Person, DrawFunc {
      
      void sing() {
        print('I can sing');
      }
    }
    

    那么这时候,虽然Teacher没有extends DrawFunc,但是如下的代码仍然可以编译通过:

    class Teacher extends Person with DrawFunc, SingFunc {
      
      String what() => "car";
      
    }
    

    而我们交换一下DrawFuncSingFunc的顺序就不行了:

    class Teacher extends Person with SingFunc, DrawFunc {
      
      String what() => "car";
      
    }
    

    提示信息是:

    Error compiling to JavaScript:
    main.dart:22:7:
    Error: 'Person' doesn't implement 'DrawFunc' so it can't be used with 'SingFunc'.
     - 'Person' is from 'main.dart'.
     - 'DrawFunc' is from 'main.dart'.
     - 'SingFunc' is from 'main.dart'.
    class Teacher extends Person with SingFunc, DrawFunc {
          ^
    Error: Compilation failed.
    

    结论:要满足on的要求,除了使用extends之外,还可以在with列表中,在它之前进行声明。在FlutterWidgetsFlutterBinding中,就涉及到了这一点的运用。

    abstract class BindingBase {}
    
    mixin ServicesBinding on BindingBase {}
    
    mixin SchedulerBinding on BindingBase, ServicesBinding {}
    
    mixin RendererBinding on BindingBase, ServicesBinding {}
    
    class WidgetsFlutterBinding extends BindingBase with ServicesBinding, SchedulerBinding, RendererBinding {}
    

    (5) Tips

    在这上面,我们接触了几个新的概念mixin, on, with

    • mixin:定义了组块。
    • on:限定了使用mixin组块的宿主必须要继承于某个特定的类;在mixin中可以访问到该特定类的成员和方法。
    • with:负责组合组块,而with后面跟的类并不一定需要是mixin的,abstract class和普通类都是可以的,这一点需要注意,例如下面这样:
    class Person {}
    
    mixin DrawFunc on Person {
      
      String content = '..';
      
      String what();
        
      void draw() {
        print('I can draw ${what()}');  
      }
      
    }
    
    mixin SingFunc on Person {
      
      void sing() {
        print('I can sing');
      }
    }
    
    abstract class DanceFunc {
      
      void dance() {
        print('I can dance');
      }
      
    }
    
    class Teacher extends Person with DrawFunc, SingFunc, DanceFunc {
      
      String what() => "car";
      
    }
    
    void main() {
      Teacher().draw();
      Teacher().sing();
      Teacher().dance();
    }
    

    4.2 冲突

    如果同时存在extends, with,并且它们都定义了相同的方法名,那么结果如何呢?我们来看下面的例子:

    class Extends {
      
      void log() {
        print('extends');
      }
      
    }
    
    mixin Mixins {
      
      void log() {
        print('mixin');
      }
      
    }
    
    mixin Mixins2 {
      
      void log() {
        print('mixin2');
      }
      
    }
    
    class Log extends Extends with Mixins, Mixins2 {}
    
    void main() {
      Log().log();
    }
    

    输出结果:

    mixin2
    

    结论

    • with修饰的会覆盖extends中修饰的同名方法。
    • with列表中后一个的会覆盖之前的。

    再来看一下加上了implements的情况。

    class Extends {
      
      void log() {
        print('extends');
      }
      
    }
    
    mixin Mixins {
      
      void log() {
        print('mixin');
      }
      
    }
    
    mixin Mixins2 {
      
      void log() {
        print('mixin2');
      }
      
    }
    
    class Implements {
      
      void log() {
        print('implements');
      }
      
    }
    
    class Log extends Extends with Mixins, Mixins2 implements Implements {}
    
    void main() {
      Log().log();
    }
    

    输出结果为:

    mixin2
    

    这里我们发现了一个奇怪的现象:虽然我们加上了implements,但是Dart居然没让我们实现Implements.log()方法!

    这是因为在这种情况下,它识别到我们从withextends中获得了log()方法的能力,因此调用的是Mixins2.log()

    假如我们对Implements#log方法进行实现:

    class Log extends Extends with Mixins, Mixins2 implements Implements {
      
      void log() {
        print("implements log");
      }
    }
    

    输出的结果为:

    implements log
    

    五、小结

    梳理下来,有几点感想:

    • 之前我们设计中用到了interface的部分,可以采用只带有抽象方法的abstract class替换,并继续使用implements关键字。
    • 理解mixin的概念,我是将它理解为一个个的功能组块:哪些宿主需要哪些功能,我就with到上去。
    • on关键字一方面是为了限制组块的应用场景,也可以为多个组块提供公共的基础功能。

    参考资料

    相关文章

      网友评论

        本文标题:Flutter 知识梳理 (Dart) - implements

        本文链接:https://www.haomeiwen.com/subject/xqxfkctx.html