flutter【7】dart语言--类

dart 是纯面向对象语言,每个对象都是某个类的实例,并且所有类都是继承自 Object。

dart中继承使用的是 mixin-based 继承机制,详细理解可以参考Flutter基础:理解Dart的Mixin继承机制

访问类的成员

使用点(.)访问实列变量或者方法

var p = Point(2, 2);

// Set the value of the instance variable y.
p.y = 3;

// Get the value of y.
assert(p.y == 3);

// Invoke distanceTo() on p.
num distance = p.distanceTo(Point(4, 4));

使用(?.)避免当左边对象为 null 时抛出异常。

// If p is non-null, set its y value to 4.
p?.y = 4;

使用构造方法

dart中构造方法的命名为 ClassName 或者 ClassName.identifier。dart2.x 版本中使用构造方法创建类实例时,new 关键字为可选项。

//省略关键字 new
var p1 = Point(2, 2);
var p2 = Point.fromJson({'x': 1, 'y': 2});
//不省略关键字 new
var p1 = new Point(2, 2);
var p2 = new Point.fromJson({'x': 1, 'y': 2});

有些类提供常量构造方法,使用常量构造方法可以创建编译时常量。使用常量构造方法需要用 const 替代 new 关键字。

var p = const ImmutablePoint(2, 2);

使用一个类的常量构造方法产生的多个编译时常量其实是同一个对象。

var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);

assert(identical(a, b)); // They are the same instance!

另外,在dart2.x版本下,常量上下文中可以省略构造方法和字面量之前的 const 关键字。

// Lots of const keywords here.
const pointAndLine = const {
  'point': const [const ImmutablePoint(0, 0)],
  'line': const [const ImmutablePoint(1, 10), const ImmutablePoint(-2, 11)],
};

// Only one const, which establishes the constant context.
const pointAndLine = {
  'point': [ImmutablePoint(0, 0)],
  'line': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)],
};

//构造方法不在常量上下文中
var a = const ImmutablePoint(1, 1); // Creates a constant
var b = ImmutablePoint(1, 1); // Does NOT create a constant

assert(!identical(a, b)); // NOT the same instance!

获取对象类型

可以使用 Object 的 runtimeType 属性来判断实例的类型,该属性返回一个 Type 对象。

print('The type of a is ${a.runtimeType}');

实例变量

声明实例变量,所有未初始化的实例变量的值都是 null。

class Point {
  num x; // Declare instance variable x, initially null.
  num y; // Declare y, initially null.
  num z = 0; // Declare z, initially 0.
}

所有实例变量都有一个隐式的 getter 方法。Non-final 实例变量还会有一个隐式的 setter 方法。

class Point {
  num x;
  num y;
}

void main() {
  var point = Point();
  point.x = 4; // Use the setter method for x.
  assert(point.x == 4); // Use the getter method for x.
  assert(point.y == null); // Values default to null.
}

如果在实例变量声明的地方就初始化(不是在构造函数中或者方法中),则该值在实例创建的时候就会被赋值,比构造方法和初始化列表执行要早。

构造方法

构造方法的名字与类名相同,可以使用一些可选标识符进行修饰(参考命名构造方法)。

class Point {
  num x;
  num y;

  Point(num x, num y) {
    // There's a better way to do this, stay tuned.
    this.x = x;//this 关键字表示当前实列的引用,在名称冲突时使用
    this.y = y;
  }
}

将构造方法的参数值赋给实例变量的使用场景很频繁,所以dart提供了语法糖来简化。

class Point {
  num x, y;

  // Syntactic sugar for setting x and y
  // before the constructor body runs.
  Point(this.x, this.y);
}

默认构造方法

构造方法不会继承,如果没有声明构造方法,则dart会提供一个默认的无参构造方法,而且会调用超类的无参构造方法。

命名构造方法

使用命名构造函数可以为一个类实现多个构造函数, 或者使用命名构造函数来更清晰的表明意图。

class Point {
  num x;
  num y;

  Point(this.x, this.y);

  // Named constructor
  Point.fromJson(Map json) {
    x = json['x'];
    y = json['y'];
  }
}

调用超类的 non-default 构造方法

默认情况下,子类调用超类的非命名、无参构造函数, 超类的构造函数在子类构造函数体开始执行的位置调用。 如果提供了一个 initializer list(初始化参数列表) ,则初始化参数列表在超类构造函数执行之前执行。下面是构造函数执行顺序:

  1. 初始化参数列表
  2. 超类无名构造方法
  3. 主类的无名构造方法

如果超类没有无名无参数构造函数, 则需要手动调用超类的其他构造函数。

class Person {
  String firstName;

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

class Employee extends Person {
  // Person does not have a default constructor;
  // you must call super.fromJson(data).
  Employee.fromJson(Map data) : super.fromJson(data) {
    print('in Employee');
  }
}

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

  // Prints:
  // in Person
  // in Employee
  if (emp is Person) {
    // Type check
    emp.firstName = 'Bob';
  }
  (emp as Person).firstName = 'Bob';
}

由于超类构造函数的参数在构造函数执行之前执行,所以 参数可以是一个表达式或者一个方法调用。如果在构造函数的初始化列表中使用 super(),需要把它放到最后。

class Employee extends Person {
  // ...
  Employee() : super.fromJson(findDefaultData());
}

初始化列表

在构造方法执行之前还可以初始化实例参数,使用逗号分隔初始化表达式,但是表达式不能使用this关键字。

class Point {
  num x;
  num y;

  Point(this.x, this.y);

  // Initializer list sets instance variables before
  // the constructor body runs.
  Point.fromJson(Map jsonMap)
      : x = jsonMap['x'],
        y = jsonMap['y'] {
    print('In Point.fromJson(): ($x, $y)');
  }
}

初始化列表适合用来初始化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 = new Point(2, 3);
  print(p.distanceFromOrigin);
}

重定向构造方法

重定向构造方法没有方法体,用来调用其他构造方法。

class Point {
  num x;
  num y;

  // The main constructor for this class.
  Point(this.x, this.y);

  // Delegates to the main constructor.
  Point.alongXAxis(num x) : this(x, 0);
}

常量构造方法

用来提供一个状态不变的实列对象,可以把这些对象定义为编译时常量。

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;

  // _cache is library-private, thanks to
  // the _ in front of its name.
  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);
  }
}

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

方法

实例方法可以访问实例变量和this。

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 方法,如果变量为 non-final ,则还有一个隐式的 setter 方法。可以通过 get、set 关键字,自定义实现getter、setter。

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

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

  // Define two calculated properties: 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;
}

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

抽象方法

抽象方法只能存在与抽象类中,没有方法体,以逗号结尾

abstract class Doer {
  // Define instance variables and methods...

  void doSomething(); // Define an abstract method.
}

class EffectiveDoer extends Doer {
  void doSomething() {
    // Provide an implementation, so the method is not abstract here...
  }
}

抽象类

使用 abstract 关键字修饰的类就是抽象类,不可以被实例化。如果定义一个工厂构造方法,让抽象类看起来像能够实例化。

抽象类一般都有抽象方法。

// This class is declared abstract and thus
// can't be instantiated.
abstract class AbstractContainer {
  // Define constructors, fields, methods...

  void updateChildren(); // Abstract method.
}

隐式接口

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

一个类可以通过 implements 关键字来实现一个或者多个接口, 并实现每个接口定义的 API。

// A person. The implicit interface contains greet().
class Person {
  // In the interface, but visible only in this library.
  final _name;

  // Not in the interface, since this is a constructor.
  Person(this._name);

  // In the interface.
  String greet(who) => 'Hello, $who. I am $_name.';
}

// An implementation of the Person interface.
class Imposter implements Person {
  // We have to define this, but we don't use it.
  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()));
}

扩展类

使用 extends 关键字创建子类,使用 super 关键字引用超类。

class Television {
  void turnOn() {
    _illuminateDisplay();
    _activateIrSensor();
  }
  // ···
}

class SmartTelevision extends Television {
  void turnOn() {
    super.turnOn();
    _bootNetworkInterface();
    _initializeMemory();
    _upgradeApps();
  }
  // ···
}

覆写

子类可以覆写实例方法、getter、setter。使用 @override 注解,表明是覆写的成员。

class SmartTelevision extends Television {
  @override
  void turnOn() {...}
  // ···
}

可覆写操作符

< + | []
> / ^ []=
<= ~/ & ~
>= * << ==
– % >>

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方法。

noSuchMethod()

使用 noSuchMethon()可以检测并响应访问不存在的方法或者实例变量的情况。

class A {
  // Unless you override noSuchMethod, using a
  // non-existent member results in a NoSuchMethodError.
  @override
  void noSuchMethod(Invocation invocation) {
    print('You tried to use a non-existent member: ' +
        '${invocation.memberName}');
  }
}

You can’t invoke an unimplemented method unless one of the following is true:

  • The receiver has the static type dynamic.
  • The receiver has a static type that defines the unimplemented method (abstract is OK), and the dynamic type of the receiver has an implemention of noSuchMethod() that’s different from the one in class Object.

枚举

使用 enum 声明,枚举的每个值都有一个 index 的getter方法,index 从0开始。

enum Color { red, green, blue }

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 语句中,但是如果不对枚举的每个值都进行处理会警告。

var 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'
}

枚举类型具有如下的限制:

  • 无法继承枚举类型、无法使用 mix in、无法实现一个枚举类型
  • 无法显示的初始化一个枚举类型

为类添加功能:mixin

mixin 是一种多继承中复用类代码的方法,通过 with 关键字来使用。

class Musician extends Performer with Musical {
  // ...
}

class Maestro extends Person
    with Musical, Aggressive, Demented {
  Maestro(String maestroName) {
    name = maestroName;
    canConduct = true;
  }
}

实现 mixin 需要使用 mixin 关键字创建一个继承 Object 的未声明构造方法的类。

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');
    }
  }
}

使用 on 关键字可以定义 mixin 所需要的超类。

mixin MusicalPerformer on Musician {
  // ···
}

类变量和方法

使用 static 关键字修饰的类级别的方法和常量,dart的静态变量命名风格为 lowerCamelCase 。

静态变量描述类的状态和常量。

class Queue {
  static const initialCapacity = 16;
  // ···
}

void main() {
  assert(Queue.initialCapacity == 16);
}

静态变量第一次被使用时才会初始化。

静态方法

静态方法不能对实例进行操作,也没有 this 的访问权限。

import 'dart:math';

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

void main() {
  var a = Point(2, 2);
  var b = Point(4, 4);
  var distance = Point.distanceBetween(a, b);
  assert(2.8 < distance && distance < 2.9);
  print(distance);
}

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

静态函数还可以当做编译时常量使用。例如,可以把静态函数当做常量构造函数的参数来使用。

猜你喜欢

转载自blog.csdn.net/weixin_34216196/article/details/87381405