Введение в конструкторы Dart

Flutter

представлять

Поскольку языки Flutter и Dart очень просты в использовании, многие студенты, которые изучают Flutter, обычно пишут страницы Flutter напрямую, и они мало знают о языке Dart, включая меня.

Язык Dart похож на многие современные языки, но его конструкторы и гибкость все же больше, чем у других известных мне языков, поэтому при написании конструкторов либо выдаются необъяснимые ошибки, либо написание очень сложное, и нет его сути освоил.

Давайте подытожим синтаксис конструкторов Dart для вашего удобства.

Введение в грамматику

Формат

Конструкторы Dart бывают 4 форматов:

  • ClassName(...) //普通构造函数
  • Classname.identifier(...) //命名构造函数
  • const ClassName(...) //常量构造函数
  • factroy ClassName(...) //工厂构造函数

использовать

Прежде чем определить наш собственный конструктор, мы коснемся использования конструктора, который используется следующим образом:

var p1 = Point(2, 2); //Dart2中,可以省略构造函数前的new
var p2 = Point.fromJson({'x': 1, 'y': 2});
var p = const ImmutablePoint(2, 2); //常量构造函数,用来创建编译期常量

In-Stream реклама
Если вы хотите узнать, к какому классу принадлежит переменная, вы можете использоватьruntimeType:
print('The type of a is ${a.runtimeType}');

определение

1. Конструктор по умолчанию

Если вы определяете класс без определения конструктора, то он будет иметь конструктор по умолчанию, этот конструкторнет параметров

Если у этого класса есть родительский класс, конструктор по умолчанию также вызовет конструктор без аргументов родительского класса.

2. Обыкновенный конструктор

Это наш обычный конструктор, который выглядит почти так же, как и в других языках.

class Point {
  num x, y;

  Point(num x, num y) {
    // There's a better way to do this, stay tuned.
    this.x = x;
    this.y = y;
  }
}

В приведенном выше примере есть только две переменные-члены, если их 10, не будет ли это проблемой? Итак, у Dart есть для вас синтаксический сахар:

class Point {
  num x, y;

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

Это может упростить присваивание x и y, поэтому нет необходимости писать тело метода конструктора.Не забудьте использовать точку с запятой после круглых скобок.

3. Именованные конструкторы


class Point {
  num x, y;

  Point(this.x, this.y);

  // 命名构造函数,新增代码
  Point.origin() {
    x = 0;
    y = 0;
  }
}

Помните, что именованные конструкторы не могут быть унаследованы.Если подкласс хочет иметь тот же именованный конструктор, что и родительский класс, то напишите именованный конструктор с тем же именем (обычно в именованном конструкторе подкласса вызывается именованный конструктор родительского класса с одноименным названием) Конструктор)

4. Порядок, в котором конструктор вызывает конструктор родительского класса

Если ваш класс наследуется от родительского класса, то конструктор подкласса неизбежно вызовет конструктор родительского класса.На данный момент есть две ситуации:

  • Язык Dart помогает вам вызывать конструктор без параметров родительского класса.
  • Явно вызывать конструктор родительского класса в коде
4.1 Вызов по умолчанию вызывает конструктор без параметров родительского класса

Если вы не вызываете конструктор суперкласса явно, а суперкласс имеет конструктор без параметров, Dart поможет вам вызвать конструктор без параметров суперкласса в верхней части тела метода конструктора подкласса. Разумеется, как мы скажем позже, конструктор разделен на несколько частей для инициализации переменных-членов, и порядок вызовов следующий:

  • список инициализации
  • Конструктор без аргументов родительского класса
  • Конструктор не аргументов для подклассов

Конечно, если родительский конструктор не является конструктором без аргументов, или этот неявный вызов Dart не соответствует вашим требованиям, то потребуется явный вызов конструктора родительского класса.

4.2 Явный вызов конструктора родительского класса

Явный вызов конструктора родительского класса должен выполняться в списке инициализации (помните, что вы видели список инициализации в C++? Слишком долго, чтобы забыть)

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
}

Список инициализаторов — это часть имени конструктора после двоеточия и до круглых скобок.

5. Список инициализации

Порядок выполнения списка инициализации находится в начале всего конструктора.Помимо вызова конструктора родительского класса, он также может инициализировать некоторые переменные-члены перед телом метода конструктора.

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

Особенно при инициализации этих переменных-членов с окончательным изменением список инициализации очень полезен, потому что в теле метода вы не можете присваивать значения переменным-членам с окончательным изменением, потому чтоКогда выполняется тело метода, окончательные измененные переменные-члены не могут быть изменены.. Многие люди делают ошибки в этом месте.

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

6. Прохождение конструктора

При определении конструктора помимо общего конструктора может быть еще несколько именованных конструкторов.Иногда между этими конструкторами есть некоторая одинаковая логика.Если они прописаны в каждом конструкторе, то это будет избыточно, поэтому функции конструкции можно передать .

class Point {
  num x, y;

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

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

Передача конструктора без тела метода вызовет другой конструктор в списке инициализации.

7. Константный конструктор

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

  final num x, y;

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

Если ваш класс создает объекты, которые никогда не изменяются, вы можете создать этот экземпляр константы во время компиляции, определить конструктор констант и убедиться, что все переменные-члены являются окончательными.

8. Конструктор Фабрики

Иногда может возникнуть необходимость не создавать каждый раз новый экземпляр класса, но в каждом случае нужен только один экземпляр:

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

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

Заводской конструктор, нет доступа к этому

Приведенный выше пример означает, что в классе есть еще один статический кеш._cacheНекоторые экземпляры класса Logger сохраняются.При создании экземпляра используется имя, переданное конструктору фабрики.Если он уже существует в кеше, используется существующий экземпляр в кеше.Если нет, создается новый экземпляр и помещается в кеш.

Таким образом, мы можем создавать экземпляры с именами UI / SYS / API и т. д., а затем при отладке, если для отключения звука экземпляра Logger с именем UI установлено значение true, журналы, связанные с пользовательским интерфейсом, не будут распечатываться, не затрагивая другие журналы. два имени. Это очень удобно?