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

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

一、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() 函数来实现每个可能的 gettersetter、 以及其他类型的函数,你可以使用 @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 的接口。此外,Dart和Java一样只支持单继承。而且Dart中没有和Java一样提供Interface字段去声明一个接口,如果想使用和Java接口一样的功能可以使用implementsMixins两种方式(Mixins方式见文末)。

  • 一个类可以通过 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: 是一种在多类继承中重用 一个类代码的方法(指能够将另一个或多个类的功能添加到您自己的类中,而无需继承这些类,也就是Dart不支持多继承,却能实现多继承功能的一种方式了)。

使用 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,该类用abstract class 或者mixin关键字修饰且没有构造函数, 不能调用 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');
    }
  }
}
mixin 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');
    }
  }
}

再看implements和mixin示例:

class A {
  void a() {
    print('a');
  }
}

class B implements A {
  @override
  void a() {
    print('override a()');
  }
}

class C {
  void c() {
    print('c');
  }
}

class E {
  String e = 'eeee';
}

class D extends A with C, E {
  void c() {
    print('c is D');
  }

  void d() {
    c();
  }
}

首先看B implements A,所以此时A相对于B来说就是一个接口,所以他要实现B中的方法。换句话说,Dart每个类都是接口
然后看D extends A with C ,D继承于A,由于单继承特性,这个时候D不能再使用extends关键字继承其他类,但是可以使用with关键字折叠其他类以实现代码重用。当属性和方法重复时,以当前类为准。比如上面例子调用Dc()方法打印的是 c is D。

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

详情,请参考 Dart2.1 mixin规范

八、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、无法继承枚举类型、无法使用 mixin、无法实现一个枚举类型
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 指令之前使用。 使用反射可以在运行时获取元数据信息。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,287评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,346评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,277评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,132评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,147评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,106评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,019评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,862评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,301评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,521评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,682评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,405评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,996评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,651评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,803评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,674评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,563评论 2 352

推荐阅读更多精彩内容

  • 类(Classes) Dart 是一种面向对象语言,包含类和基于mixin的继承两部分。每个对象是一个类的实例,并...
    Longshihua阅读 451评论 0 0
  • 类 Dart是一种面向对象的语言,带有类和基于mixin的继承。每个对象都是类的实例,所有类都是从对象继承而来。基...
    pstommy阅读 1,657评论 0 0
  • 这是16年5月份编辑的一份比较杂乱适合自己观看的学习记录文档,今天18年5月份再次想写文章,发现简书还为我保存起的...
    Jenaral阅读 2,746评论 2 9
  • Dart是面向对象语言,它是单继承的。所有类派生自Object 使用类成员 类成员和java一样,分为成员方法和成...
    suikaJY阅读 573评论 0 0
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,096评论 1 32