Function

В Dart функции также являются объектами — это отличает его от других языков программирования.

Функции — объекты первого класса

Dart — полностью объектно-ориентированный язык: сами функции являются объектами с типом Function.

Их можно присваивать переменным и передавать как аргументы другим функциям;

Экземпляры классов можно вызывать напрямую как функции

1 Стандартное объявление функции

// Полное объявление с аннотациями типа возвращаемого значения и параметров
bool isNoble(int atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}Code language: JavaScript (javascript)

Полный пример

// Таблица соответствия атомных номеров благородных газов
final Map<int, String> _nobleGases = {
  2: 'He',
  10: 'Ne',
  18: 'Ar',
  36: 'Kr',
  54: 'Xe',
  86: 'Rn',
};

// Полное объявление с типом возврата и типами параметров
// Тип int не поддерживает null; используйте int? для необязательных параметров
bool isNoble(int? atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}

void main() {
  print(isNoble(null));       // null не является ключом карты → false
  print(isNoble(2));          // Соответствует благородному газу → true
  print(isNoble(9));          // Совпадений нет → false
  print(isNoble(86));         // Соответствует благородному газу → true
}Code language: PHP (php)
false
true
false
trueCode language: JavaScript (javascript)

2 Пропуск аннотаций типов

Руководство по стилю Effective Dart предписывает использовать полные явные аннотации типов для публичных внешних API.

Для внутренних приватных функций аннотации типов можно опускать

Пример


final Map&lt;int, String> _nobleGases = {
  2: 'He',
  10: 'Ne',
  18: 'Ar',
  36: 'Kr',
  54: 'Xe',
  86: 'Rn',
};


isNoble(atomicNumber) {
  return _nobleGases&#91;atomicNumber] != null;
}

void main() {
  print(isNoble(null));       // null не является ключом карты → false
  print(isNoble(2));          // Соответствует благородному газу → true
  print(isNoble(9));          // Совпадений нет → false
  print(isNoble(86));         // Соответствует благородному газу → true
}

Code language: PHP (php)

В примере выше функция isNoble не указывает возвращаемый тип bool и тип параметра int?, но работает корректно.

Если типы не указаны, Dart выполняет автоматический вывод типов:

  • Параметр atomicNumber: без явного типа → выводится как dynamic (динамический тип, принимает любые значения: числа, строки, null)
  • Возвращаемое значение: без аннотации типа в начале → Dart определяет возвращаемый тип как bool по выражению после return, без записи в коде
Почему публичные API обязательно должны иметь явные типы?

Читаемость: разработчики, вызывающие вашу функцию, сразу видят типы входных и выходных данных, не читая внутренний код для догадок;

Безопасность типов: тип dynamic отключает статическую проверку; передача неверных типов не вызовет ошибку компиляции и часто приводит к падениям во время выполнения;

Подсказки IDE: аннотации типов активируют автодополнение и подсказки по параметрам в редакторе; без типов IDE не может показать точные предложения.

Когда можно опускать аннотации типов?

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

Сокращенная стрелковая синтаксическая запись (=>)

Если тело функции (код в фигурных скобках {}) содержит только одно возвращающее выражение, можно использовать стрелковую запись.

Пример

bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;Code language: PHP (php)
  • => expr эквивалентно { return expr; };
  • Между стрелкой и точкой с запятой можно писать только выражения; операторы как if запрещены (используйте тернарный оператор).
// Таблица соответствия атомных номеров благородных газов
final Map<int, String> _nobleGases = {
  2: 'Гелий',
  10: 'Неон',
  18: 'Аргон',
  36: 'Криптон',
  54: 'Ксенон',
  86: 'Радон',
};

// Сокращенная стрелковая функция: возвращает выражение сразу в одну строку, эквивалентна блочной функции с return
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;

void main() {
  // Проверка существующих благородных газов
  print(isNoble(2));   // true
  print(isNoble(18));  // true

  // Проверка обычных элементов
  print(isNoble(6));   // false (Углерод)
  print(isNoble(99));  // false (соответствующего элемента нет)
}Code language: PHP (php)

Параметры

Функции поддерживают обязательные позиционные параметры, после которых выбирается один из двух вариантов: именные параметры или опциональные позиционные параметры — их нельзя смешивать.

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

1. Именные параметры Named parameters

Ограничены фигурными скобками {}, по умолчанию необязательные; добавьте required для принудительной передачи.

1) Простые необязательные именные параметры (без значения по умолчанию, значение по умолчанию — null, тип помечается знаком ?)

void enableFlags({bool? bold, bool? hidden}) {}
// При вызове явно указывайте имена параметров
enableFlags(bold: true, hidden: false);
Code language: JavaScript (javascript)

Полный пример

/// Включить флаги стиля текста: жирный, скрытый
void enableFlags({bool? bold, bool? hidden}) {
  print('Жирный: $bold, Скрытый: $hidden');
}

void main() {
  // Передать все именные параметры
  enableFlags(bold: true, hidden: false);

  // Передать только один именной параметр, второй будет null
  enableFlags(bold: true);

  // Передать только hidden
  enableFlags(hidden: true);

  // Не передавать никаких параметров, оба значения равны null
  enableFlags();
}Code language: JavaScript (javascript)

Результат выполнения

D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
Жирный: true, Скрытый: false
Жирный: true, Скрытый: null
Жирный: null, Скрытый: true
Жирный: null, Скрытый: nullCode language: JavaScript (javascript)

2) Установка константных значений по умолчанию на этапе компиляции (не требуется знак ?, так как есть значение по умолчанию)

void enableFlags({bool bold = false, bool hidden = false}) {}
enableFlags(bold: true); // hidden автоматически принимает false
Code language: JavaScript (javascript)

Пример

/// Включить флаги стиля текста: жирный, скрытый
void enableFlags({bool bold = false, bool hidden = false}) {
  print('Жирный: $bold, Скрытый: $hidden');
}

void main() {
  enableFlags();//Параметры не переданы, оба используют значение по умолчанию false → Жирный: false, Скрытый: false
}Code language: JavaScript (javascript)

3) Обязательные именные параметры с required (Примечание: широко используются в конструкторах компонентов Flutter. Пример ниже — код Flutter, подробнее смотрите в туториалах Flutter.)

const Scrollbar({super.key, required Widget child});
// Параметры required также поддерживают необязательные типы
const Scrollbar({super.key, required Widget? child});
Code language: JavaScript (javascript)

Dart не требует размещать именные параметры в конце списка; их можно вставлять в любое место списка параметров

repeat(times: 2, () {})Code language: CSS (css)
2. Опциональные позиционные параметры Optional positional parameters

Ограничены квадратными скобками [], при пропуске принимают значение null; можно задать константные значения по умолчанию.

// Без значения по умолчанию, тип помечен знаком ?
String say(String from, String msg, &#91;String? device]) {
  var result = '$from says $msg';
  if (device != null) result = '$result with a $device';
  return result;
}
assert(say('Bob', 'Howdy') == 'Bob says Howdy');
assert(say('Bob', 'Howdy', 'smoke signal') == 'Bob says Howdy with a smoke signal');
Code language: PHP (php)

Полный пример

// [String? device] опциональный позиционный параметр без значения по умолчанию, равен null при пропуске
String say(String from, String msg, [String? device]) {
  var result = '$from says $msg';
  if (device != null) {
    result = '$result with a $device'; //Добавить информацию об устройстве к строке, если device не равен null
  }
  return result;
}

void main() {
  // Пропустить третий опциональный параметр
  String res1 = say('Bob', 'Howdy');
  print(res1);

  // Передать третий опциональный позиционный параметр
  String res2 = say('Bob', 'Howdy', 'smoke signal');
  print(res2);

  // Вручную передать null опциональному параметру
  String res3 = say('Alice', 'Hi', null);
  print(res3);
}Code language: JavaScript (javascript)

Задание значений по умолчанию:

String say(String from, String msg, &#91;String device = 'carrier pigeon']) {
  return '$from says $msg with a $device';
}
assert(say('Bob', 'Howdy') == 'Bob says Howdy with a carrier pigeon');
Code language: PHP (php)

Полный пример

// Опциональный позиционный параметр со значением по умолчанию carrier pigeon (почтовый голубь)
String say(String from, String msg, [String device = 'carrier pigeon']) {
  return '$from says $msg with a $device';
}

void main() {
  // Пропустить третий опциональный параметр, автоматически используется значение по умолчанию carrier pigeon
  String res1 = say('Bob', 'Howdy');
  print(res1); //Bob says Howdy with a carrier pigeon

  // Передать пользовательское устройство для переопределения значения по умолчанию
  String res2 = say('Bob', 'Howdy', 'smoke signal');
  print(res2); //Bob says Howdy with a smoke signal

  String res3 = say('Alice', 'Hello', 'mobile phone');
  print(res3); // Alice says Hello with a mobile phone

  String res4 = say('Jack', 'Tom', 'website foxdevelop.com');
  print(res4); // Jack says Tom with a website foxdevelop.com
}Code language: JavaScript (javascript)

Результат выполнения

Built firstdart:firstdart.
Bob says Howdy with a carrier pigeon
Bob says Howdy with a smoke signal
Alice says Hello with a mobile phone
Jack says Tom with a website foxdevelop.comCode language: JavaScript (javascript)


Входная функция main ()

Любая программа Dart должна содержать верхнеуровневую функцию main() — это единственная точка входа в программу.

  1. Базовая версия без параметров
void main() {
  print('Hello, welcome to foxdevelop.com!');
}
Code language: JavaScript (javascript)
Hello, welcome to foxdevelop.com!Code language: CSS (css)
  1. Консольная программа с аргументами запуска
// Команда запуска: dart run firstdart.dart 1 test
void main(List&lt;String> arguments) {
  print(arguments); // &#91;1, test]
}
Code language: JavaScript (javascript)

Для разбора сложных консольных аргументов используйте официальную библиотеку args.

Замените firstdart.dart выше на имя вашего Dart-файла


Функции как объекты первого класса First-class objects

1 Передача функций как параметров

void printElement(int element) =&gt; print(element);
var list = &#91;1, 2, 3];
list.forEach(printElement); // Передать функцию напрямую
Code language: PHP (php)

Полный пример

void main() {
    void printElement(int element) => print(element);
    var list = [1, 2, 3];
    list.forEach(printElement); // Передать функцию напрямую
}Code language: PHP (php)

Вывод

1
2
3

2 Присвоение анонимных функций переменным

void main() {
    var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
    print(loudify('hello')); //!!! HELLO !!! 
}Code language: JavaScript (javascript)

Типы функций Function Type

Можно явно объявлять типы переменных-функций

Формат: ТипВозврата Function(СписокПараметров, {ИменныеПараметры})

void greet(String name, {String greeting = 'Hello'}) =&gt; print('$greeting $name!');
// Объявить переменную функционального типа
void Function(String, {String greeting}) g = greet;
g('Dash', greeting: 'Howdy');
Code language: JavaScript (javascript)

С помощью typedef создавайте псевдонимы функциональных типов для повышения переиспользуемости и читаемости.

{String greeting}) → один именной параметр greeting типа String

void main() {
    void greet(String name, {String greeting = 'Hello'}) => print('$greeting $name!');
    // Объявить переменную функционального типа
    void Function(String, {String greeting}) g = greet;
    g('Jack', greeting: 'Howdy');
}Code language: JavaScript (javascript)

Вывод

Howdy Jack!

Анонимные функции (Лямбда / Замыкание Closure)

Функции без собственного имени, два варианта записи:

  1. Блочная запись (многострочная)
void main() {
    const list = ['apples', 'bananas'];
    var uppercaseList = list.map((item) {
      return item.toUpperCase();
    }).toList();
    print(uppercaseList);
}Code language: PHP (php)

(item) { return item.toUpperCase(); } в коде выше — это анонимная функция (лямбда)

Вывод: [APPLES, BANANAS]

  1. Сокращенная стрелковая запись (однострочное выражение)
void main() {
    const list = &#91;'fox', 'develop','foxdevelop.com'];
    var uppercaseList = list.map((item) => item.toUpperCase()).toList();
    uppercaseList.forEach((item) => print('$item: ${item.length}'));
}
Code language: PHP (php)

Вывод

FOX: 3
DEVELOP: 7
FOXDEVELOP.COM: 14Code language: HTTP (http)

Лексическая область видимости Lexical scope

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

bool topLevel = true;
void main() {
  var insideMain = true;
  void myFunction() {
    var insideFunction = true;
    void nestedFunction() {
      var insideNestedFunction = true;
      // Все доступны
      assert(topLevel &amp;&amp; insideMain &amp;&amp; insideFunction &amp;&amp; insideNestedFunction);
    }
  }
}
Code language: JavaScript (javascript)

Лексические замыкания Lexical closures

Замыкание: объект функции может захватывать и хранить переменные внешней области видимости и обращаться к ним даже после выхода из исходной области.

Function makeAdder(int addBy) {
  // Захват внешней переменной addBy
  return (int i) =&gt; addBy + i;
}

void main() {
  var add2 = makeAdder(2);
  var add4 = makeAdder(4);
  assert(add2(3) == 5);
  assert(add4(3) == 7);
}
Code language: PHP (php)

Пошаговый разбор выполнения вышеприведенного кода

Шаг 1: var add2 = makeAdder(2);

  1. Вызвать makeAdder, передать addBy = 2
  2. Создать анонимную функцию: (int i) => 2 + i
  3. Присвоить эту функцию переменной add2. С этого момента add2 — объект функции с телом (int i) => 2 + i; он принимает целое число i и возвращает i+2
  4. Выполнение функции makeAdder завершается, но анонимная функция навсегда запоминает addBy=2

Шаг 2: var add4 = makeAdder(4);

  1. Повторно вызвать makeAdder, передать addBy = 4
  2. Создать совершенно новую независимую анонимную функцию: (int i) => 4 + i
  3. Присвоить add4, которая хранит свою независимую копию addBy=4 без влияния на add2. То есть значение addBy хранится отдельно в двух объектах-функциях add2 и add4, один хранит 2, другой — 4
  • Вызов makeAdder(2) создает независимое замыкание с единственным привязанным значением addBy = 2, присвоенное add2;
  • Вызов makeAdder(4) создает совершенно новое отдельное замыкание с единственным привязанным значением addBy = 4, присвоенное add4;
  • add2 и add4 — полностью независимые объекты-функции, каждый хранит в закрытом виде захваченную переменную addBy без взаимного влияния.

Шаг 3: Вызвать add2(3)

Выполнить внутреннюю функцию: 2 + 3 = 5, утверждение выполняется

Шаг 4: Вызвать add4(3)

Выполнить внутреннюю функцию: 4 + 3 = 7, утверждение выполняется

Функциональные Tear-offs

Если вызывать функцию / метод без скобок, создается tear-off (легковесное замыкание), заменяющее рукописные анонимные лямбды и упрощающее код.

Это рекомендуемая запись: достаточно передать функцию напрямую.

var charCodes = &#91;68, 97, 114, 116];
var buffer = StringBuffer();
charCodes.forEach(print); // Функциональный tear-off
charCodes.forEach(buffer.write); // Методный tear-off
Code language: PHP (php)

Длинная эквивалентная запись

charCodes.forEach((code) =&gt; print(code));
charCodes.forEach((code) =&gt; buffer.write(code));
Code language: PHP (php)

Проверка равенства функций

  • Верхнеуровневые функции, статические методы: оператор == возвращает true при полном совпадении имен функций;
  • Методы экземпляров: только tear-offs, привязанные к одному и тому же экземпляру объекта, считаются равными.
void foo() {}
class A {
  static void bar() {}
  void baz() {}
}
void main() {
  var x = foo;
  assert(foo == x);

  x = A.bar;
  assert(A.bar == x);

  var v = A(), w = A(), y = w;
  x = w.baz;
  assert(y.baz == x); // Один экземпляр → равны
  assert(v.baz != w.baz); // Разные экземпляры → не равны
}
Code language: JavaScript (javascript)

Возвращаемые значения Return values

  1. Любая функция имеет возвращаемое значение: если явного return нет, в конец кода неявно добавляется return null;
foo() {}
assert(foo() == null);
Code language: JavaScript (javascript)
  1. Возврат нескольких значений: использовать кортежи Record
(String, int) foo() {
  return ('something', 42);
}
Code language: JavaScript (javascript)

Аксессоры Getter & Setter

Чтение или присваивание любого свойства по сути вызывает соответствующий getter или setter; обычные переменные автоматически создают неявные аксессоры, их также можно явно описать вручную.

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

String _secret = 'Hello';

// Getter для чтения
String get secret {
  print('Вызван getter');
  return _secret.toUpperCase();
}

// Setter для присваивания
set secret(String newMsg) {
  print('Вызван setter');
  if (newMsg.isNotEmpty) _secret = newMsg;
}

void main() {
  print(secret); // Чтение, активирует getter → HELLO
  secret = 'Dart is fun'; // Присвоение, активирует setter
  print(secret); // DART IS FUN
}
Code language: JavaScript (javascript)

* Генераторы Generators

Лениво создают последовательности по запросу, два вида: синхронные и асинхронные, используют yield для выдачи значений.

  1. Синхронный генератор sync*, возвращает Iterable
Iterable&lt;int> naturalsTo(int n) sync* {
  int k = 0;
  while (k &lt; n) yield k++;
}
Code language: HTML, XML (xml)
  1. Асинхронный генератор async*, возвращает Stream
Stream&lt;int> asyncNaturalsTo(int n) async* {
  int k = 0;
  while (k &lt; n) yield k++;
}
Code language: JavaScript (javascript)
  1. Оптимизация рекурсивных генераторов yield* (делегирует другому генератору для повышения производительности)
Iterable&lt;int> naturalsDownFrom(int n) sync* {
  if (n &gt; 0) {
    yield n;
    yield* naturalsDownFrom(n - 1);
  }
}
Code language: HTML, XML (xml)

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *