「 Dart 」Dart 中的类

这是我参与11月更文挑战的第6天,活动详情查看:2021最后一次更文挑战

在 Dart 中所有对象都是某个类的实例,所有类继承了 Object 类。

构造函数

import 'dart:math';

// 定义类
class Point {
  num x = 0, y = 0;

  // Point(this.x, this.y); // 构造器

  // 或者
  Point(x, y) {
    this.x = x;
    this.y = y;
    print('这是构造函数,在实例化时触发');
  }

  // 实例方法
  num distanceTo(Point other) {
    var dx = x - other.x;
    var dy = y - other.y;
    return sqrt(dx * dx + dy * dy);
  }
}

main() {
  // 调用类的构造函数,new 可以省略
  Point p1 = new Point(1, 2); // 这是构造函数,在实例化时触发
  Point p2 = Point(3, 4); // 这是构造函数,在实例化时触发
  // 调用类方法
  print(p1.distanceTo(p2)); // 2.8284271247461903
}
复制代码

由于 Dart 2.12 的特性,这样写构造函数会报错:

class Point {
  num x, y;
  Point(x, y) {  // 不可为空的实例字段 'x' 必须被初始化
      			// 尝试添加一个初始化表达式,或者在这个构造函数中添加一个字段初始化器,或者标记它
    this.x = x;
    this.y = y;
  }
}
复制代码

由于空安全,Dart 不知道你是否为 x,y 分配了变量。该写法在之前的版本没有问题,解决方法是dart - Non-nullable instance field must be initialized - Stack Overflow

解决方法之一:

class Point {
  num? x, y;
  Point(x, y) {
    this.x = x;
    this.y = y;
  }
}
复制代码

num?表示可空类型,表示变量值可以为空。

使用命名构造函数可以为一个类实现多个构造函数, 或者使用命名构造函数使代码语义化,之前使用的 DateTime.now() 也是命名构造函数。

使用重定向构造函数,用冒号调用其他构造函数。

class Point {
  num? x;
  num? y;

  Point.initX(y) { // 命名构造函数
    this.x = 2;
    this.y = y;
  }

  Point.redirctor(num x) : this(x, 0); // 重定向构造函数
}

main() {
  Point p1 = Point.initX(2); 
  print('${p1.x}, ${p1.y}'); // 2, 2
  Point p2 = Point.redirctor(1); 
  print('${p2.x}, ${p2.y}'); // 1, 0
}
复制代码

代码越来越多导致维护性越来越差,所以我们需要把类抽离成文件,在需要的地方使用import导入库。

// lib\Point.dart
class Point {
  num x = 0, y = 0;
  Point(this.x, this.y); 
}

// main.dart
import 'lib/Point.dart';
main(){
	//...
}
复制代码

类及成员可见性

Dart 中的可见性以 library 为单位,每个Dart 文件就是一个库。

使用_表示属性或方法的私有属性,相当于 Java 中的privite

class Point {
  num _x = 0, _y = 0;
  Point(this.x, this.y); 
}
复制代码

当其被抽离成一个文件时,私有属性的作用才生效。

getter,setter

每个变量都有其默认的gettersetter方法,final 声明的变量只可读,只有 getter 方法。

在 Dart 中,方法不能重载。

class Rect {
  late num width, height;
  Rect(this.width, this.height);
  area() {
    return this.width * this.height;
  }

  // getter
  get area1 {
    return this.width * this.height;
  }

  // setter
  set height2(value) {
    this.height = value;
  }
}

main() {
  Rect r1 = Rect(1, 2);
  print(r1.area()); // 2
  print(r1.area1); // 2
  r1.height2 = 3;
  print(r1.area1); // 3
}
复制代码

初始化列表

初始化列表是在实例化之前进行的操作:

class Rect {
  late num width, height;
  // Rect(this.width, this.height); 不能同时使用
  Rect()
      : width = 2,
        height = 3 {
    print('in class');
    print(this.width * this.height);
  }
}

main() {
  Rect r1 = Rect();
  print('in main');
}

// 输出结果
// in class
// in main
// 6
复制代码

static 静态成员

静态变量和静态方法可以通过类来访问,而不是类的实例。

class Person {
  static String name = 'Jackie';
  static void show() {
    print(name);
  }
}

main() {
  print(Person.name); //Jackie
  Person.show(); // Jackie
}
复制代码

静态的方法不可以访问非静态的成员,非静态的方法可以访问静态的成员。

class Person {
  static String name = 'Jackie';
  static show() {
    print(name);
  }
  showInfo() {
    print(name);
    show();
  }
}

main() {
  Person p = Person();
  p.showInfo(); // Jackie Jackie
}
复制代码

继承

  1. 子类使用extends关键词来继承父类;

  2. 子类会继承父类里除构造函数的可见的属性和方法;

  3. 子类能复写父类的方法。

class Person {
  late String name;
  late int age;
  Person(this.name, this.age);
  work() {
    print('$name is working.');
  }
}

class Web extends Person {
  late String sex;
  // 子类的构造函数
  Web(String name, int age, String sex) : super(name, age) {
    this.sex = sex;
  }
  // 子类可以复写父类的方法
  @override // 表示覆写父类的方法,选写
  work() {
    print('$name is working 996.');
  }
}

main() {
  Web w = Web('Tom', 20, 'male');
  print(w.sex); // male
  w.work(); // Tom is working 996.
}
复制代码

抽象类

Dart抽象类主要用于定义标准,子类可以继承抽象类,也可以实现抽象类接口。

  1. 抽象类通过 abstract 关键字来定义。
  2. Dart 中没有方法体的方法称为抽象方法。
  3. 如果子类继承抽象类必须要实现里面的抽象方法。
  4. 如果把抽象类当做接口实现的话必须要实现抽象类里面定义的所有属性和方法。
  5. 抽象类不能被实例化,只有继承它的子类可以。

extends 抽象类和 implements 的区别:

  1. 如果要复用抽象类里面的方法,并且要用抽象方法约束自类的话我们就用extends继承抽象类。
  2. 如果只是把抽象类当做标准的话就用 implements 实现抽象类。
abstract class Person {
  late String name;
  late int age;
  Person(this.name, this.age);
  work(); // 抽象方法
}

class Employee extends Person {
  Employee(String name, int age) : super(name, age);
  @override
  work() {
    print('$name is working 996.');
  }
}

class Employee2 extends Person {
  Employee2(String name, int age) : super(name, age);
  @override
  work() {
    print('$name is working 669.');
  }
}

main() {
  Person e = Employee('Tom', 20);
  e.work(); // Tom is working 996.
  // Person p = Person(); //报错,抽象类不能实例化
  Person e2 = Employee2('Jerry', 20);
  e2.work(); // Jerry is working 669.
}
复制代码

多态

是子类的实例赋值给父类的引用,允许将子类类型的指针赋值给父类类型的指针, 同一个函数调用会有不同的执行效果 。

多态就是父类定义一个方法,让继承他的子类去实现,每个子类有不同的表现。

上述例子中,Employee 和 Employee2 类都实现了多态。

接口

Dart 也有接口,但是和 Java 有区别。

Dart 的接口没有 interface 关键字,而是普通类或抽象类都可以作为接口使用 implements 关键字被实现。

如果实现的类是普通类,会将普通类和抽象类中的属性的方法全部需要覆写一遍.而因为抽象类可以定义抽象方法,普通类不可以,所以建议使用抽象类定义接口。

abstract class Db {
  late String uri;
  add(String data);
}

class Mysql implements Db {
  @override
  late String uri;

  Mysql(this.uri);

  @override
  add(String data) {
    print('This is Mysql. ' + '$data');
  }
}

main() {
  Mysql m = Mysql('xxx');
  m.add('123');
}
复制代码

接口抽离成不同的文件:

// lib\Db.dart
abstract class Db {...}

// lib\Mysql.dart
import 'lib/Db.dart';
class Mysql implements Db {...}

// main.dart
import 'lib/Mysql.dart';
main() {...}
复制代码

一个类可以实现多个接口

class C implements A, B {
    // A, B 接口所有的属性和方法
}
复制代码

Mixins

Class 中无法实现多继承,Mixins 不同于继承和接口,可以实现类似多继承的功能:

class C extends A, B {} // 报错
class C with A, B {...} // √
复制代码
  1. 作为 Mixins 的类只能继承自Object,不能继承其他类。
  2. 作为 Mixins 的类不能有构造函数。
  3. 一个类可以 Mixins 多个 Mixins 类。
  4. Mixins 绝不是继承,也不是接口,而是一种全新的特性。
class A {...}
class B extends A {...}
class C {...}
class D with B, c {...} // 报错
class D extends A with B, C {...} // √
复制代码

后写的类会覆盖掉前面的类:

class C with A, B {...} // B 的方法会覆盖掉 A
class D extends A with B, C {...} // B 的方法会覆盖掉 A,C 的方法会覆盖掉 A 和 B
复制代码

猜你喜欢

转载自juejin.im/post/7033409280237633550