Dart 笔记 8 - 类(1)

Dart 是一种面向对象的语言,具有类和基于 mixin 的继承。每个对象都是一个类的实例,所有的类都是 Object 的子类。

基于 mixin 的继承意味着,尽管每个类(除了 Object )都只有一个超类,但类主体可以在多个类层次结构中重用。

无论数字、函数和 null 都是对象。

new 关键字可选。尽量不用。

构造函数

class Point {
  num x, y;

  Point(num x, num y) {
    this.x = x;
    this.y = y;
  }
}

等价的更简单的写法

class Point {
  num x, y;

  // 没有函数体直接分号结束
  Point(this.x, this.y);
}

如果不声明构造函数,则提供默认构造函数。默认构造函数没有参数,并在超类中调用无参数构造函数。

子类不从父类继承构造函数。没有声明构造函数的子类只有默认的构造函数(没有参数,没有名称)而不是从父类继承的构造函数。Java 是必须继承父类构造函数。

命名的构造函数

使用命名构造函数可以在一个类中定义多个构造函数,或者让一个类的作用对于开发人员来说更清晰:

class Point {
  num x, y;

  // 一个构造函数
  Point(this.x, this.y);

  // 一个命名的构造函数
  Point.origin() {
    x = 0;
    y = 0;
  }
}

调用非默认的超类构造函数

默认情况下,子类中的构造函数调用父类的未命名的无参数构造函数。父类的构造函数在构造函数体的开始处被调用。如果类中有使用初始化列表,初始化列表将在调用超类之前执行。

如果超类没有未命名的无参数构造函数,则必须手动调用超类中的一个构造函数。在冒号 : 之后,在构造函数体(如果有的话)之前指定超类构造函数。

class Person {
  String firstName;

  Person.fromJson(Map data) {
    print('in Person');
  }
}

class Employee extends Person {
  // Person 类没有默认的无参无命名的构造函数
  // 必须调用 super.fromJson(data)
  Employee.fromJson(Map data) : super.fromJson(data) {
    print('in Employee');
  }
}

main() {
  var emp = Employee.fromJson({});

  // 输出
  // in Person
  // in Employee
  if (emp is Person) { // 类型检查
    emp.firstName = 'Bob';
  }
  (emp as Person).firstName = 'Bob';
}

初始化列表

除了调用超类构造函数之外,还可以在构造函数主体运行之前初始化实例变量。初始值设定项用逗号分开。

Point.fromJson(Map<String, num> json)
    : x = json['x'],
      y = json['y'] {
  print('In Point.fromJson(): ($x, $y)');
}

初始化器的右边部分中无法访问 this 关键字。

在开发期间,可以通过在初始化列表中使用 assert 来验证输入。

Point.withAssert(this.x, this.y) : assert(x >= 0) {
  print('In Point.withAssert(): ($x, $y)');
}

初始化列表在设置 final 字段时很方便。下面的示例初始化初始化列表中的三个 final 字段:

import 'dart:math';

class Point {
  final num x;
  final num y;
  final num distanceFromOrigin;

  Point(x, y)
      : x = x,
        y = y,
        distanceFromOrigin = sqrt(x * x + y * y);
}

main() {
  var p = Point(2, 3);
  print(p.distanceFromOrigin); // 3.605551275463989
}

重定向构造函数

class Point {
  num x, y;

  Point(this.x, this.y);

  // 调用自己的另一个构造函数
  Point.alongXAxis(num x) : this(x, 0);
}

常量构造函数

如果类生成的对象不会改变,可以使这些对象成为编译时常量。为此,定义一个 const 构造函数,并确保所有实例变量都是 final 的。

class ImmutablePoint {
  static final ImmutablePoint origin =
      const ImmutablePoint(0, 0);

  final num x, y;

  const ImmutablePoint(this.x, this.y);
}

工厂构造函数

在实现构造函数时使用 factory 关键字,该构造函数并不总是创建类的新实例。例如,工厂构造函数可以从缓存返回实例,也可以返回子类型的实例。

以下示例演示工厂构造函数从缓存返回对象:

class Logger {
  final String name;
  bool mute = false;

  // 缓存对象
  static final Map<String, Logger> _cache =
      <String, Logger>{};

  factory Logger(String name) {
    if (_cache.containsKey(name)) {
      return _cache[name]; // 缓存里有就取出缓存
    } else {
      final logger = Logger._internal(name);
      _cache[name] = logger;
      return logger;
    }
  }

  Logger._internal(this.name);

  void log(String msg) {
    if (!mute) print(msg);
  }
}

工厂构造函数不能访问 this 关键字。

调用工厂构造函数,就像调用其他构造函数一样:

var logger = Logger('UI');
logger.log('Button clicked');

方法

实例方法

实例方法和 Java 一样。

import 'dart:math';

class Point {
  num x, 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);
  }
}

getter/setter

所有实例变量都生成隐式 getter 方法。非最终实例变量也生成隐式 setter 方法。

class Rectangle {
  num left, top, width, height;

  Rectangle(this.left, this.top, this.width, this.height);

  // 定义两个属性,right 和 bottom,它们的 getter 和 setter 需要计算,不能用默认的了
  num get right => left + width;
  set right(num value) => left = value - width;
  num get bottom => top + height;
  set bottom(num value) => top = value - height;
}

void main() {
  var rect = Rectangle(3, 4, 20, 15);
  assert(rect.left == 3);
  rect.right = 12;
  assert(rect.left == -8);
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 爬虫笔记1-抓取

    如果 Response 的 status_code 为 200,即 requests.codes.ok,表明请求下载成功。请求成功的话,下载的页面作为一个字符串...

    七适散人
  • Android 动画总结(6) - 估值器

    七适散人
  • Android 优化——网络优化

    七适散人
  • Kotlin的面向对象编程,深入讨论继承写法的问题

    很多人可能都不知道,或者是已经忘记这件事了,但是我自己承诺过要写的东西,我是不会忘记的。

    用户1158055
  • C#基础知识系列五(构造函数)

      2、不带参数的构造函数称为“默认构造函数”。 无论何时,只要使用 new 运算符实例化对象,并且不为 new 提供任何参数,就会调用默认构造函数。除非类是s...

    aehyok
  • 构造函数浅析

    构造函数浅析<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" ...

    ternturing
  • 关于Java构造函数(Constructor)的常见问题总结1 为什么调用子类的构造方法的时候,默认会调用父类的构造方法2 常见错误:Implicit super constructor is und

    当继承自一个类的时候,构造方法就会首先调用super()方法。如果没有显式的写这个语句,那么编译器就会自动插入这个语句。这就是为什么我们上面的那个例子程序会先调...

    desperate633
  • C#类学习-3

    只要使用 new 运算符实例化对象,并且不为 new 提供任何参数,就会调用默认构造函数。

    py3study
  • C#-构造函数

    创建一个类的时候,使用new运算符对类进行实例化。在为新对象分配内存之后,new运算符立即调用构造函数。

    祝你万事顺利
  • Kotlin入门(12)类的概貌与构造

    上一篇文章提到泛型函数appendString是在类外面定义,这不免使人疑惑,类里面又该怎样定义成员函数呢?为解答这个疑问,接...

    用户4464237

扫码关注云+社区

领取腾讯云代金券