Вот и подошли к одной из интересных тем - ООП. Принципы здесь как и во многих других языках программирования.
// Определяем класс
class CompetitorWait {
// Определяем поля внутри класса задавая им тип
int number;
int timestamp;
// Конструктор должен совпадать по наименованию с именем класса и будет выполнен при создании объекта, при этом мы можем указать входные переменные как обязательные, так и необязательные в квадратных скобках. В теле конструктор указываем что с ними делать
CompetitorWait ( int number , [ int timestamp = 0 ] ) {
this.number = number;
this.timestamp = timestamp;
}
// Мы можем переопределить дефолтные функции объекта, например что выводить при печати
@override
String toString ( ) {
return 'Number = [$number] Start = [$timestamp]';
}
}
// Программа
void main ( ) {
var sportsmenWait = CompetitorWait ( 15 , 1600000000000 );
print ( sportsmenWait ); // Number = [15] Start = [1600000000000]
}Аннотация @override применяется всегда когда мы хотим переопределить методы базового класса.
Наследование задается через ключевое слово extends. При этом важно: конструкторы не наследуются.
// Создаем класс и наследуем от предыдущего
class CompetitorFixed extends CompetitorWait {
bool isSync = false;
// Собственный конструктор, который обращается к конструктору родителя через super, но безусловно можно этого не делать
CompetitorFixed ( { number , timestamp } ) : super ( number , timestamp );
@override
String toString ( ) {
return 'Number = [$number] Fixed = [$timestamp] Sync = [${isSync ? 'OK' : '?'}]';
}
}
// Программа
void main ( ) {
var sportsmenFix = CompetitorFixed ( number : 17 , timestamp : 1600000000001 );
print ( sportsmenFix ); // Number = [17] Fixed = [1600000000001] Sync = [?]
}Усложним процесс создания - добавим именованные конструкторы, т.е. мы можем указать какой именно конструктор использовать при создании объекта. Расширим предыдущий класс:
// Подключим математический пакет, он будет необходим для функции с рандомом
import 'dart:math';
class CompetitorFixed extends CompetitorWait {
bool isSync = false;
CompetitorFixed ( { number , timestamp } ) : super ( number , timestamp );
// Именованный конструктор
CompetitorFixed.timefix ( number ) : super ( number , new DateTime.now().millisecondsSinceEpoch );
// Для примера обращения через функцию и через собственный основной конструктор (this), который всё также выводит на родительский
CompetitorFixed.random ( ) : this ( _randomNumber ( ) , new DateTime.now().millisecondsSinceEpoch ) {
this.isSync = true;
}
// Для примера вычислений с инициализаторами
CompetitorFixed.randomMicroseconds ( microseconds ) : this.isSync = microseconds % 10 > 4 , super ( _randomNumber ( ) ) {
print ( microseconds );
this.timestamp = microseconds ~/ 1000;
}
// Функция
static int _randomNumber ( ) {
return Random ( ).nextInt ( 80 ) + 20;
}
@override
String toString ( ) {
return 'Number = [$number] Fixed = [$timestamp] Sync = [${isSync ? 'OK' : '?'}]';
}
}
// Программа
void main ( ) {
var sportsmenFixTime = CompetitorFixed.timefix ( 18 );
print ( sportsmenFixTime ); // Number = [18] Fixed = [1600426352438] Sync = [?]
sportsmenFixTime.isSync = true;
print ( sportsmenFixTime ); // Number = [18] Fixed = [1600426352438] Sync = [OK]
var sportsmenFixRandom = CompetitorFixed.random ( );
print ( sportsmenFixRandom ); // Number = [64] Fixed = [1600426352438] Sync = [OK]
var sportsmenFixRandomMicroseconds = CompetitorFixed.randomMicroseconds ( new DateTime.now().microsecondsSinceEpoch ); // 1600426352438000
print ( sportsmenFixRandomMicroseconds ); // Number = [64] Fixed = [1600426352438] Sync = [?]
}Также бывают константные конструкторы
// Константные конструкторы
class ImmutablePoint {
final num x, y;
const ImmutablePoint(this.x, this.y);
}
// Программа
void main ( ) {
var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);
var c = ImmutablePoint(1, 1);
var d = ImmutablePoint(1, 1);
print ( a == b ); // true
print ( b == c ); // false
print ( c == d ); // false
}Особенность таких конструкторов - они не содержат тело.
Можно было заметить что полями класса выступают константы определённые через final. Такие переменные должны быть заданы изначально, либо до выполнения конструктора, т.е. либо передаваться в параметрах, либо задаваться в инициализаторе конструктора.
class ClassConstants {
final num x;
final num y;
final num z = 1;
ClassConstants (this.x) : this.y = 2 {
//this.z = 3; // так уже нельзя
};
}Фабричные конструкторы позволяют не создавать каждый раз новый объект, а использовать уже существующий
// Фабричные конструкторы
class Logger {
final String name;
static final Map<String, Logger> _cache =
<String, Logger>{};
factory Logger(String name) {
return _cache.putIfAbsent(
name, () => Logger._internal(name));
}
Logger._internal(this.name) {
print ( 'Call constructor: ${this.name}' );
}
}
// Программа
void main ( ) {
Logger logger1 = Logger ( 'test' ); // Call constructor: test
Logger logger2 = Logger ( 'testing' ); // Call constructor: testing
Logger logger3 = Logger ( 'test' );
Logger logger4 = Logger ( 'test!' ); // Call constructor: test!
}