Начиная с версии 3.0 в языке Dart появилась такая возможность как records. Records являются анонимным, неизменяемым типом, который, подобно типам коллекций, позволяет объединять несколько объектов в один объект. В отличие от других типов коллекций, records имеют фиксированный размер, могут хранить значения разных типов и типизированы. В других языках программирования есть аналогичная структура данных - кортежи
Например, определим простейший кортеж:
void main() { var person = ("Tom", 38); print(person); // ("Tom", 38) }
Здесь определен кортеж person, который состоит из двух элементов - строки "Tom" и целого числа 38. Для определения кортежа применяются круглые скобки, внутри которых через запятую перечисляются элементы кортежа. Мы можем вывести содержимого этого кортежа с помощью функции print.
Стоит отметить, что кортежи - типизированы, они имеют определенный тип, который определяет, данные каких типов и в какой последовательности имеются в кортеже. Так, в примере выше для определения кортежа применялось ключевое слово var, при котором тип кортежа выводился автоматически исходя из присваемого значения. Но мы также можем явным образом указать тип:
void main() { (String, int) person = ("Tom", 38); print(person); }
Кортеж ("Tom", 38)
представляет тип (String, int)
. Тип кортежа также определяется с помощью кавычек, внутри которых указываются типы вложеннных элементов.
Причем порядок имеет значение. Так, тип (String, int)
указывает, что вначале в кортеже должна идти строка и только потом число, а не наоборот. Например, следующее определение
кортежа будет ошибочным:
(String, int) person = (22, "Tom"); // ! Ошибка
Подобным образом можно определять кортежи с большим количеством элементов. Например:
void main() { (String, int, String) person = ("Tom", 38, "Google"); print(person); }
По умолчанию для обращения к элементам кортежа используются номера элементов в кортеже, которые предваряются символом $ (нумерация начинается с 1):
void main() { var person = ("Tom", 38); print(person.$1); // Tom print(person.$2); // 38 }
Например, выражение person.$1
представляет обращение к первому элементу кортежа person, а person.$2
- к второму элементу.
Но также для каждого элемента кортежа можно указать имена:
void main() { var person = (name:"Tom", age:38); print(person.name); // Tom print(person.age); // 38 }
В этом случае сначала указывается имя и через двоеточие значение элемента. Стоит отметить, что в этом случае тип кортежа будет ({int age, String name})
:
void main() { ({int age, String name}) person = (name:"Tom", age:38); print(person.name); // Tom print(person.age); // 38 }
И в случае с именованными элементами их порядок роли не играет. Можно комбинировать именованные и анонимные элементы, но в этом случае именованные элементы идут после анонимных:
void main() { var person = ("Tom", age:38); print(person.$1); // Tom print(person.age); // 38 }
Рассмотрим сценарии, где кортежи нам могут пригодиться. Традиционно функции в языке Dart могли возвращать только одно значение. Если же надо было возвратить из функции несколько значений, то их надо было либо упаковывать их в другие типы данных, например, в списки, либо определять новые классы, которые могли бы хранить подобные множественные значения. И кортежи как раз позволяют решить эту проблему:
void main() { var person = defaultPerson(); print(person); // ("Tom", 22) } (String, int) defaultPerson(){ return ("Tom", 22); }
Здесь для простоты функция defaultPerson просто возвращает кортеж из двух элементов. В функции main результат функции defaultPerson можно присвоить переменной person.
Также можно возвращать кортеж с именованными элементами:
void main() { var person = defaultPerson(); print(person.name); // ("Tom", 22) } ({String name, int age}) defaultPerson(){ return (name:"Tom", age:22); }
Еще один сценарий представляет передачу в функцию множества значений. И опять же здесь мы можем объединить все значения в кортеж:
void main() { createPerson(("Bob", 42)); } void createPerson((String, int) data){ print("Name: ${data.$1}"); print("Age: ${data.$2}"); }
Передача кортежа с именованными элементами:
void main() { createPerson((name:"Sam", age:27)); } void createPerson(({String name, int age}) data){ print("Name: ${data.name}"); print("Age: ${data.age}"); }
Кортежи сравниваются поэлементно в порядке следования элементов:
void main() { var person = ("Tom", 38); var tom = ("Tom", 38); var tomas = (name:"Tom", age:38); print(person == tom); // true print(person == tomas); // false }