美文网首页FlutterDart语法糖
(七)Dart Methods(函数)、抽象、扩展类、枚举、Me

(七)Dart Methods(函数)、抽象、扩展类、枚举、Me

作者: 小哥_xiaoge | 来源:发表于2019-05-26 23:51 被阅读65次

    函数:是类中定义的方法,是类对象的行为。

    一、Instance methods(实例函数)

    对象的实例函数可以访问this。 例如下面示例中的 distanceTo() 函数 就是实例函数:

    import 'dart:math';
    
    class Point {
      num x;
      num y;
      Point(this.x, this.y);
    
      num distanceTo(Point other) {
        var dx = x - other.x;
        var dy = y - other.y;
        return sqrt(dx * dx + dy * dy);
      }
    }
    

    1.1.Getters and setters

    Getters 和 setters 是用来设置和访问对象属性的特殊 函数。每个实例变量都隐含的具有一个 getter, 如果变量不是 final 的则还有一个 setter。 你可以通过实行 getter 和 setter 来创建新的属性, 使用 getset 关键字定义 getter 和 setter:

    class Rectangle {
      num left;
      num top;
      num width;
      num height;
    
      Rectangle(this.left, this.top, this.width, this.height);
    
      // 定义两个计算属性: right and bottom.
      num get right             => left + width;
          set right(num value)  => left = value - width;
      num get bottom            => top + height;
          set bottom(num value) => top = value - height;
    }
    
    main() {
      var rect = new Rectangle(3, 4, 20, 15);
      assert(rect.left == 3);
      rect.right = 12;
      assert(rect.left == -8);
    }
    
    • 借助于 getter 和 setter ,你可以直接使用实例变量,并且在不改变客户代码的情况下把他们包装成方法。

    注意: 像 (++) 这种操作符不管是否定义 getter 都会正确的执行。 为了避免其他副作用, 操作符只调用 getter 一次,然后 把其值保存到一个临时变量中。

    二、Abstract methods(抽象函数)

    实例函数、 getter、和 setter 函数可以为抽象函数, 抽象函数是只定义函数接口但是没有实现的函数,由子类来 实现该函数。如果用分号来替代函数体,则这个函数就是抽象函数。

    abstract class Doer {
      // ...定义实例变量和函数...
    
      void doSomething(); // 定义抽象函数.
    }
    
    class EffectiveDoer extends Doer {
      void doSomething() {
        // ...抽象函数在此实现后,这里的函数就不再是抽象的...
      }
    }
    

    注意:调用一个没实现的抽象函数会导致运行时异常。

    三、Overridable operators(可覆写的操作符)

    下表中的操作符可以被覆写。 例如,如果你定义了一个 Vector 类, 你可以定义一个 + 函数来实现两个向量相加。

    操作符 操作符 操作符 操作符 操作符 操作符
    < + | [] > /
    ^ []= <= ~/ & ~
    >= * << == - %
    >>

    下面是覆写了 + 和 - 操作符的示例:

    class Vector {
      final int x;
      final int y;
      const Vector(this.x, this.y);
    
      /// Overrides + (a + b).
      Vector operator +(Vector v) {
        return new Vector(x + v.x, y + v.y);
      }
    
      /// Overrides - (a - b).
      Vector operator -(Vector v) {
        return new Vector(x - v.x, y - v.y);
      }
    }
    
    main() {
      final v = new Vector(2, 3);
      final w = new Vector(2, 2);
    
      // v == (2, 3)
      assert(v.x == 2 && v.y == 3);
    
      // v + w == (4, 5)
      assert((v + w).x == 4 && (v + w).y == 5);
    
      // v - w == (0, 1)
      assert((v - w).x == 0 && (v - w).y == 1);
    }
    
    • 如果覆写了 == ,则还应该覆写对象的 hashCode getter 函数。

    Dart 中的每个对象都有一个整数 hash 码,这样每个对象都 可以当做 map 的 key 来用。但是,你可以覆写 hashCode getter 来自定义 hash 码的实现,如果你这样做了,你也需要 同时覆写 == 操作符。相等的对象(使用 == 比较)的 hash 码应该一样。Hasm 码并不要求是唯一的, 但是应该具有良好的分布形态。

    class Person {
      final String firstName, lastName;
    
      Person(this.firstName, this.lastName);
    
      // 覆写 hashCode 的策略和 Effective Java第11章节一样.
      int get hashCode {
        int result = 17;
        result = 37 * result + firstName.hashCode;
        result = 37 * result + lastName.hashCode;
        return result;
      }
    
      // 如果覆写 hashCode,通常应该实现操作符 ==.
      bool operator ==(other) {
        if (other is! Person) return false;
        Person person = other;
        return (person.firstName == firstName &&
            person.lastName == lastName);
      }
    }
    
    main() {
      var p1 = new Person('bob', 'smith');
      var p2 = new Person('bob', 'smith');
      var p3 = 'not a person';
      var p4 = new Person('jim', 'smith');
      
      print(p1.hashCode == p2.hashCode); // true
      print(p1 == p2); // true
      print(p1 == p3); // false
    
      print(p1.hashCode == p4.hashCode); // false
      print(p1 == p4); // false
    }
    

    关于覆写的更多信息请参考下面的 扩展类

    四、Extending a class(扩展类)

    • 使用 extends 定义子类, supper 引用 超类:
    class Television {
      void turnOn() {
        _illuminateDisplay();
        _activateIrSensor();
      }
      // ...
    }
    
    class SmartTelevision extends Television {
      void turnOn() {
        super.turnOn();
        _bootNetworkInterface();
        _initializeMemory();
        _upgradeApps();
      }
      // ...
    }
    
    • 子类可以覆写实例函数,getter 和 setter。 下面是覆写 Object 类的 noSuchMethod() 函数的例子, 如果调用了对象上不存在的函数,则就会触发 noSuchMethod() 函 数。
    class A {
        // 如果你不重写 noSuchMethod 方法, 就用一个不存在的成员,
       // 会导致NoSuchMethodError 错误。
        void noSuchMethod(Invocation mirror) {
            print('You tried to use a non-existent member:' + 
                '${mirror.memberName}');
         }
      }
    
    • 使用 @override 注解来 表明你的函数是想覆写超类的一个函数:
    class A {
      @override
      void noSuchMethod(Invocation mirror) {
        // ...
      }
    }
    
    • 使用 noSuchMethod() 函数来实现每个可能的 getter 、setter、 以及其他类型的函数,你可以使用 @proxy 注解来避免警告信息:
    @proxy
    class A {
      void noSuchMethod(Invocation mirror) {
        // ...
      }
    }
    
    • 若知道编译时的具体类型,则可以实现这些类来避免警告,和使用 @proxy 效果一样:
    class A implements SomeClass, SomeOtherClass {
      void noSuchMethod(Invocation mirror) {
        // ...
      }
    }
    

    五、Abstract classes(抽象类)

    抽象类 : 一个不能被实例化的类。

    • 使用 abstract 修饰符定义一个 抽象类 ,抽象类通常用来定义接口, 以及部分实现。如果你希望你的抽象类 是可实例化的,则定义一个 工厂构造函数。

    • 抽象类通常具有 抽象函数 下面是定义具有抽象函数的抽象类的示例:

    // 这个类是抽象类,因此不能被实例化。
     abstract class AbstractContainer {
       // ...定义构造函数,域,方法...
    
       void updateChildren(); // 抽象方法。
     }
    
    • 下面的类不是抽象的,但是定义了一个抽象函数,这样 的类是可以被实例化的:
    class SpecializedContainer extends AbstractContainer {
        // ...定义更多构造函数,域,方法...
    
        void updateChildren() {
          // ...实现 updateChildren()...
        }
    
       // 抽象方法造成一个警告,但是不会阻止实例化。
       void doSomething();
     }
    

    六、Implicit interfaces(隐式接口)

    每个类都隐式的定义了一个包含所有实例成员的接口, 并且这个类实现了这个接口。如果你想 创建类 A 来支持 类 B 的API,而不想继承 B 的实现, 则类 A 应该实现 B 的接口。

    • 一个类可以通过 implements 关键字来实现一个或者多个接口, 并实现每个接口定义的 API。 例如:
    // Person类 ,包含 greet() 隐式接口。
     class Person {
         // 在这个接口中,只有库中可见。
         final _name;
    
         // 不在接口中,因为这是个构造函数。
         Person(this._name);
    
         // 在接口中。
         String greet(who) => 'Hello, $who. I am $_name.';
     }
    
     //  Person 接口的一个实现。
     class Imposter implements Person {
         // 我们不得不定义它,但不用它。
         final _name = "";
    
         String greet(who) => 'Hi $who. Do you know who I am?';
     }
    
     greetBob(Person person) => person.greet('bob');
    
     main() {
        print(greetBob(new Person('kathy')));
        print(greetBob(new Imposter()));
     }
    
    • 下面是实现多个接口 的示例:
    class Point implements Comparable, Location {
      // ...
    }
    

    七、Adding features to a class: mixins(为类添加新的功能)

    Mixins: 是一种在多类继承中重用 一个类代码的方法。

    使用 with 关键字后面为一个或者多个 mixin 名字来使用 mixin。 下面是示例显示了如何使用 mixin:

    class Musician extends Performer with Musical {
      // ...
    }
    
    class Maestro extends Person
        with Musical, Aggressive, Demented {
      Maestro(String maestroName) {
        name = maestroName;
        canConduct = true;
      }
    }
    
    • 定义一个类继承 Object,该类没有构造函数, 不能调用 super ,则该类就是一个 mixin。例如:
    abstract class Musical {
      bool canPlayPiano = false;
      bool canCompose = false;
      bool canConduct = false;
    
      void entertainMe() {
        if (canPlayPiano) {
          print('Playing piano');
        } else if (canConduct) {
          print('Waving hands');
        } else {
          print('Humming to self');
        }
      }
    }
    

    注意: 从 Dart 1.13 开始, 这两个限制在 Dart VM 上 没有那么严格了:
    1、 Mixins 可以继承其他类,不再限制为继承 Object
    2、 Mixins 可以调用 super()
    3、这种 “super mixins” 还 无法在 dart2js 中使用 并且需要在 dartanalyzer 中使用 --supermixin 参数。

    详情,请参考 Mixins in Dart。

    八、Enumerated types(枚举类型)

    枚举类型:通常称之为 enumerations 或者 enums, 是一种特殊的类,用来表现一个固定 数目的常量。

    • 使用 enum 关键字来定义枚举类型
    enum Color {
      red,
      green,
      blue
    }
    
    • 枚举类型中的每个值都有一个 index getter 函数, 该函数返回该值在枚举类型定义中的位置(从 0 开始)。 例如,第一个枚举值的位置为 0, 第二个为 1.
    assert(Color.red.index == 0);
    assert(Color.green.index == 1);
    assert(Color.blue.index == 2);
    
    • 枚举的 values 常量可以返回 所有的枚举值。
    List<Color> colors = Color.values;
    assert(colors[2] == Color.blue);
    
    • switch 语句中使用枚举。 如果在 switch (*e*) 中的 e 的类型为枚举类, 如果你没有处理所有该枚举类型的值的话,则会抛出一个警告:
    enum Color {
      red,
      green,
      blue
    }
    // ...
    Color aColor = Color.blue;
    switch (aColor) {
      case Color.red:
        print('Red as roses!');
        break;
      case Color.green:
        print('Green as grass!');
        break;
      default: // Without this, you see a WARNING.
        print(aColor);  // 'Color.blue'
    }
    

    枚举类型具有如下的限制:
    1、无法继承枚举类型、无法使用 mix in、无法实现一个枚举类型
    2、无法显示的初始化一个枚举类型

    九、Class variables and methods(类变量和函数)

    使用 static 关键字来实现类级别的变量和函数。

    9.1.Static variables(静态变量)

    • 静态变量在第一次使用的时候才被初始化。
    • 静态变量对于类级别的状态是 非常有用的:
    class Color {
         static const red = const Color('red'); // 一个恒定的静态变量
         final String name;      // 一个实例变量。 
         const Color(this.name); // 一个恒定的构造函数。
      }
    
      main() {
         assert(Color.red.name == 'red');
      }
    

    9.2.Static methods(静态函数)

    • 静态函数不再类实例上执行, 所以无法访问 this。例如:
    import 'dart:math';
    
    class Point {
      num x;
      num y;
      Point(this.x, this.y);
    
      static num distanceBetween(Point a, Point b) {
        var dx = a.x - b.x;
        var dy = a.y - b.y;
        return sqrt(dx * dx + dy * dy);
      }
    }
    
    main() {
      var a = new Point(2, 2);
      var b = new Point(4, 4);
      var distance = Point.distanceBetween(a, b);
      assert(distance < 2.9 && distance > 2.8);
    }
    
    • 静态函数还可以当做编译时常量使用。例如, 你可以把静态函数当做常量构造函数的参数来使用。

    注意:对于通用的或者经常使用的静态函数,考虑 使用顶级方法而不是静态函数。

    十、Metadata(元数据)

    使用元数据给你的代码添加其他额外信息。 元数据注解是以 @ 字符开头,后面是一个编译时 常量(例如 deprecated)或者 调用一个常量构造函数。

    有三个注解所有的 Dart 代码都可以使用: @deprecated@override、 和 @proxy。关于 @override@proxy 示例请参考上面的扩展类。 下面是使用 @deprecated 的 示例:

    class Television {
      /// _Deprecated: Use [turnOn] instead._
      @deprecated
      void activate() {
        turnOn();
      }
    
      /// Turns the TV's power on.
      void turnOn() {
        print('on!');
      }
    }
    

    你还可以定义自己的元数据注解。 下面的示例定义了一个带有两个参数的 @todo 注解:

    library todo;
    
    class todo {
      final String who;
      final String what;
    
      const todo(this.who, this.what);
    }
    

    使用 @todo 注解的示例:

    import 'todo.dart';
    
    @todo('seth', 'make this do something')
    void doSomething() {
      print('do something');
    }
    

    元数据可以在 library、 class、 typedef、 type parameter、 constructor、 factory、 function、 field、 parameter、或者 variable 声明之前使用,也可以在 import 或者 export 指令之前使用。 使用反射可以在运行时获取元数据信息。

    相关文章

      网友评论

        本文标题:(七)Dart Methods(函数)、抽象、扩展类、枚举、Me

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