Функции и методы классов :: Cетевой уголок Majestio

Функции и методы классов


В данном разделе будут рассмотрены функции. Методы будут раccмотрены только в теме объявлений входных аргументов.

Важное замечание!
В языке программирования Dart примитивные типы int, double, bool и String передаются по значению, остальные типы - передаются по ссылке!

Входные аргументы функции Dart подразделяются на:

  • позиционные
  • именованные
  • необязательные позиционные
  • необязательные именованные
  • комбинация из вышеперечисленных

Позиционные аргументы

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

void myFunction(String name, int date, String monthName) {
  print('$name родился $date $monthName!');
}

void main(List<String> arguments) {
  myFunction('Александр', 10, 'сентября');
}

// будет напечатано: Александр родился 10 сентября!

Именованные аргументы

Для объявления того, что на вход функции аргументы передаются именованным образом необходимо обернуть их в фигурные скобки «{ }». После чего, в момент вызова функции явно указать какому именованному аргументу какое значение передается. Если тип объявляемого аргумента null-safety, т.е. не может хранить значение null, то перед объявляемым аргументом используйте ключевое слово required.

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

  1. аргументу функции передали переменную, хранящую значение null
  2. именованному аргументу ничего не передавали, вследствие чего значение аргумента по умолчанию становится равным null
void myFunction({
  String? name,
  required int date,
  required String monthName,
}) {
  if (name != null) { 
    return '$name родился $date $monthName!'; 
  } 
  return 'Не установлено имя новорожденного!';
}

void main(List<String> arguments) {
  myFunction(date: 10, name: 'Александр', monthName: 'сентября',);
  myFunction(date: 10, monthName: 'сентября',);
}

// будет напечатано: Александр родился 10 сентября!
// будет напечатано: Не установлено имя новорожденного!

Значения могут передаваться в произвольной последовательности, так как мы явно указываем какому из аргументов функции будет соответствовать то или иное значение.

Когда мы указываем, что именованный аргумент может принимать значение null, он становится необязательным при вызове функции. Но когда, кровь из носу, запрещено давать такие вольности разработчику, который будет использовать ваш код – нужно пометить такой именованный аргумент, как required. Тогда у него не останется вариантов, кроме как самому передать такому именованному аргументу вызываемой функции null.

Необязательные позиционные аргументы

Для того, чтобы указать необязательные аргументы при их позиционном размещении, нужно обернуть их в квадратные скобки [ ]:

String myFunction(String name, int date, [String? monthName]) {
  if (monthName != null) {
    return '$name родился $date $monthName!';
  }
  return '$date числа, неустановленного месяца, родился $name!';
}

void main(List<String> arguments) {
  print(myFunction('Александр', 20));
  print(myFunction('Александр', 20, 'мая'));
} 
// будет напечатано: 20 числа неустановленного месяца родился Александр!
// будет напечатано: Александр родился 20 мая!

Необязательные аргументы функции по умолчанию

Когда мы объявляем необязательные позиционные или именованные аргументы, они по умолчанию инициализируются значением null (когда при вызове функции им не задается отличное от null значение). Для того, чтобы присвоим им значение по умолчанию, отличное от null, после объявления имени аргумента нужно добавить оператор присваивание и само значение, которое будет использоваться в коде функции всякий раз, пока явно не передастся другое значение. При этом не имеет значения, тип объявляемого аргумента – null-safety или нет:

String myFunction(
  String name, [
  int? date = 10,
  String monthName = 'июля',
]) {
  return '$name родился $date $monthName!';
}

void main(List<String> arguments) {
  print(myFunction('Александр'));
  print(myFunction('Александр', 24));
  print(myFunction('Александр', 20, 'мая'));
} 
// будет напечатано: Александр родился 10 июля! 
// будет напечатано: Александр родился 24 июля! 
// будет напечатано: Александр родился 20 мая!

Чтобы ввести значение по умолчанию для именованных аргументов, достаточно просто присвоить ему значение и не использовать ключевое слово required. К расположению значений по умолчанию для такого типа аргументов требований нет, но лучше придерживаться устоявшейся традиции и объявлять их в конце:

String myFunction({
  required String name,
  required String monthName,
  int date = 10,
}) {
  return '$name родился $date $monthName!';
}

void main(List<String> arguments) {
  print(myFunction(name: 'Александр', monthName: 'мая'));
  print(myFunction(
    date: 14,
    name: 'Александр',
    monthName: 'мая',
  ));
} 
// будет напечатано: Александр родился 10 мая! 
// будет напечатано: Александр родился 14 мая!

В языке программирования Dart функцию можно присваивать переменной, а так же передавать ее в качестве аргумента. Речь идет именно о передаче ее, а не вызове в месте передачи. Это всё можно посмотреть в следующем коде. В комментариях это отмечено цифрами:
  1. Пример присвоение функции переменной
  2. Пример использовании функции как аргумента
  3. Пример использовании анонимной функции (лямбды) как аргумента
int addFunc(int a, int b) {
  return a + b;
}

String seasonFunc(int month) {
  return switch (month) {
    == 12 || > 0 && < 3 => 'Зима',
    >= 3 && < 6 => 'Весна',
    >= 6 && < 9 => 'Лето',
    >= 9 && < 12 => 'Осень',
    _ => "Хрень в аргументах!",
  };
}

String myStrFunc(
  String prefix,
  int month,
  String Function(int) func,
) {
  return prefix + ': ' + func(month);
}

int subFunc(
  int a,
  int b, {
  int c = 10,
  int Function(int, int) func = addFunc,
}) {
  return c - func(a, b);
}

void main() {
  var season = seasonFunc;                         // 1. Пример присвоение функции переменной
  print(myStrFunc('Сезон', 12, season));           // 2. Пример использовании функции как аргумента
  print(myStrFunc("Сезон", 0, season));
  print(subFunc(3, 7));
  print(subFunc(2, 4, c: 2));
  print(subFunc(2, 4, c: 2, func: (int a, int b) { // 3. Пример использовании анонимной функции (лямбды) как аргумента
    return a * b;
  }));
}
Результат работы будет следующий:
Сезон: Зима
Сезон: Хрень в аргументах!
0
-4
-6
Еще один пример:
int sub(
  int a,
  int b, {
  int c = 10,
  int Function(int, int)? func,
}) {
  if (func == null) {
    return 0;
  }
  return c - func(a, b);
}

void main() {
  var a = 13, b = 12;
  print(sub(
    2,
    4,
    c: 2,
    func: a < b
        ? (int a, int b) {
            return a * b;
          }
        : (int a, int b) {
            return a - b;
          },
  ));
}
Вывод:
4

Когда сложные типы становятся чрезвычайно длинные и слишком громоздкие по описанию - их удобно "именовать" алиасами. Именовать можно не только сложные типы данных, но и функции. Вот пример:
typedef TypeMyMap = Map<String, Map<(int, List<int>), int>>; // 1. Алиас для сложного типа
typedef TypeMyMapFunc = int Function(TypeMyMap)?;            // 2. Алиас для функции со сложным типом аргумента

int myFunc(TypeMyMap data) {
  var sum = 0.0;
  for (var MapEntry(:value) in data.entries) {
    for (var MapEntry(key: recKey, value: recValue) in value.entries) {
      var (int a, List<int> b) = recKey;
      sum += (a * b.reduce((value, element) => value + element)) / recValue;
    }
  }
  return sum.floor();
}

int add(
  int a,
  TypeMyMap data, {
  TypeMyMapFunc func,
}) {
  if (func == null) {
    return 0;
  }
  return a + func(data);
}

void main() {
  TypeMyMap map = {
    'a': {
      (1, [1, 2, 3]): 100,
      (2, [2, -4, 9]): -98,
      (3, [3, 4, 5]): 3,
    },
    'b': {
      (10, [1, 0, 3]): 100,
      (20, [6, -4, 2]): -98,
      (30, [-3, 4, -5]): 3,
    }
  };
  print(add(22, map, func: myFunc));
}
Будет напечатано:
-7

Анонимные функции

Анонимные функции - часто называют просто "лямбды". Это функции без имени. Часто просто "встраиваются" в точке их исполнения. Простой пример из учебника:
void main(List<String> arguments) { 
  var myList = ['Привет!', 'Я', '-', 'анонимная', 'функция!']; 
  myList.forEach((item) { 
      print( 'По индексу ${myList.indexOf(item)} хранится значение => $item' ); 
    }
  ); 
}
Будет напечатано:
По индексу 0 хранится значение => Привет! 
По индексу 1 хранится значение => Я 
По индексу 2 хранится значение => - 
По индексу 3 хранится значение => анонимная
По индексу 4 хранится значение => функция!
В данном примере анонимная функция имеет следующую реализацию:
(item) { // блок с входными аргументами, аргументы могут отсутствовать 
  // блок тела функции
}

Стрелочные функции

Стрелочная функция предоставляет форму для объявления однострочных именованных или анонимных функций. Их поведение аналогично обычным функциям.
int add(int a, int b) => a + b; 

void main(List<String> arguments) { 
  print(add(1,2)); 
}
Будет напечатано:
3

Важное замечание!
У стрелочных функций есть особенность - по умолчанию они всегда возвращают значение, то есть оператор return в этих функциях не используется, но его наличие подразумевается.

Тем не менее, мы можем переписать пример выше, где возвращаемое будет просто игнорироваться:
void main(List<String> arguments) { 
  var myList = ['Привет!', 'Я', '-', 'анонимная', 'функция!']; 
  myList.forEach((item) => print( 'По индексу ${myList.indexOf(item)} хранится значение => $item' )); 
}
Вывод в консоль будет аналогичен:
По индексу 0 хранится значение => Привет! 
По индексу 1 хранится значение => Я 
По индексу 2 хранится значение => - 
По индексу 3 хранится значение => анонимная
По индексу 4 хранится значение => функция!
Рейтинг: 0/5 - 0 голосов