Коротко о Dart: часть первая

В этом (и следующем) параграфе мы будем говорить о Dart. Наша цель — в первую очередь освежить знания. В этом параграфе поговорим о:

  • переменных;
  • коллекциях;
  • строках;

Но сперва — давайте вспомним, что нам известно о самом Dart.

Этот язык программирования появился в 2011 году как замена JavaScript. То есть — если вы знакомы с web-программированием, то найдёте в Dart схожие с JavaScript возможности. На сегодняшний день язык прошёл достаточно долгий путь, получил поддержку null-safety (мы вскользь упоминали её в предыдущих параграфах), статическую типизацию и Dart-VM.

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

Переменные

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

Создание переменной мало чем отличается от других языков. Пример:

Код выше создаёт переменную типа String, присваивает ей значение Hello, world! и выводит результат в консоль.

var

Dart поддерживает вывод типов (type-inference), так что указывать тип переменной сразу необязательно. На помощь приходит ключевое слово var:

dynamic

В Dart есть ключевое слово dynamic, отключающее проверки типов для переменной. Им стоит пользоваться, если тип переменной не известен до запуска программы, и его нельзя вывести. Давайте заменим var из предыдущего примера на dynamic и слегка модифицируем код:

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

Заметим, что в случае с var вычисление типа переменной всегда происходит на этапе компиляции. Следовательно, если переменная, помеченная var, не будет инициализирована при объявлении, то такая переменная будет иметь «неизвестный тип» (null), что позволит присвоить ей значение любого типа.

Рассмотрим описанное поведение на примере ниже:

final and const

Во всех примерах выше переменные можно модифицировать и переопределять после объявления. Но чаще всего при разработке появляется потребность запретить изменение переменной после определения. Например, хорошей практикой считается делать свойства модели, и следовательно, саму модель неизменяемыми. А константные объекты позволяют выиграть в производительности.

final

Ключевое слово final запрещает переопределение переменной после инициализации.

Важно. Переменная, помеченная final, — не константа, просто её значение нельзя переопределить после инициализации.

Давайте создадим переменную final и попытаемся изменить её значение.

Действительно, переменная a получит значение 5, а при дальнейших попытках переприсвоить ей значение мы увидим исключение на этапе компиляции.

При этом можно модифицировать внутреннее состояние значения, присвоенного переменной final.

late

Ключевое слово late позволяет объявить переменную и не инициализировать её значение сразу.

Это полезно, когда хотим объявить переменную non-nullable (о null-safety поговорим в следующем параграфе), а значение ей задать позже.

1//Без late будет исключение compile-time
2late String name;
3
4void main() {
5  name = 'Dart';
6  print('Hello, $name');
7}

const

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

А пока давайте заменим final на const в примере выше.

В итоге получаем сразу несколько ошибок до компиляции:

  1. Переменную const нельзя оставить неинициализированной;
  2. Переменную const нельзя переопределить;
  3. Переменной const нельзя изменить тип, что довольно очевидно и вытекает из предыдущего пункта и системы типов языка.

Теперь давайте решим задачу.

Задача

Создадим два списка const и final и попробуем добавить в них новые элементы. Что выведет программа?

1void main() {
2  final finalList = <int>[];
3  finalList.add(3);
4  print(finalList);
5
6  const constList = <int>[];
7  constList.add(2);
8  print(constList);
9}
Ответ [3]

Ошибка — Unsupported operation

Коллекции

В этом блоке рассмотрим работу с коллекциями.

Объявление коллекций

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

11final list = [1, "string", 2.4]; // любой тип
22final strongTypedList = <int>[1, 2, "string"]; // только целочисленные значения, из-за "string" получаем ошибку на этапе компиляции
3

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

Он будет считать, что в внутри списка list лежат переменные dynamic. В итоге код не гарантирует Type-Safety.

Распаковка коллекций

Мы можем распаковать несколько коллекций в одну с помощью оператора ....

1final collection1 = [1,2,3];
2final collection2 = [4,5,6];
3final compositeCollection = [...collection1, ...collection2];

If-else внутри коллекций

При объявлении коллекций мы можем внести в неё условия на добавление элементов.

Попробуйте переключить condition на false и посмотреть на результат.

Строки

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

Как создать строку

В Dart есть несколько способов, чтобы создать строку:

  1. Через одинарные кавычки 'string';
  2. Через двойные кавычки "string";
  3. Через тройные кавычки — одинарные '''string''' или двойные """string""";
  4. Как «сырую» строку r'string';
  5. Через StringBuffer . В основном он нужен для манипуляций со строками.

Общепринятым является первый способ. Однако, это не значит, что другие избыточны. Давайте рассмотрим следующий пример:

На первый взгляд, последний пример с «сырой» строкой (r'Hello, world! I like dollar$$$$’) может показаться непонятным. Давайте разберёмся.

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

Интерполяция строк

Выше вы могли заметить использование символа $ при выводе значений. Это интерполяция строк — одна из возможностей языка, позволяющих манипулировать со строками.

В Dart есть три способа, чтобы объединить их в одну:

  1. Конкатенация — склеивание двух строк в одну;
  2. Интерполяция — встраивание в строку вычисленных значений;
  3. StringBuffer.

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

Сильной разницы в производительности между ними нет, так что вы вольны выбирать именно тот, который удобнее в вашей ситуации.

Экранирование спецсимволов

Когда мы рассматривали способы, которыми создаются строки, то сказали, что можно «отключить» все спецсимволы в сырой строке. Но такой способ далеко не всегда нам нужен, зачастую мы хотим экранировать только пару символов из всей строки.

Достичь этого можно с помощью ещё одного спецсимвола — \.

1final productCost = 400;
2
3// Итог: Hi i'm Michael. This thing costs only $400
4final constString = 'Hi i\'m Michael. This thing costs only \$$productCost';
5

Регулярные выражения

В Dart есть удобный механизм по работе с регулярными выражениями — RegExp.

Приведём пример, который проверит, есть ли в строке слово world:

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


На этом всё. В следующем параграфе мы продолжим вспоминать основы Dart — сфокусируемся на функциях и null-safety, но не забудем и про другие важные концепции — модификаторы доступа, typedef и другие.

Чтобы добавить в заметки выделенный текст, нажмите Ctrl + E

Отмечайте параграфы как прочитанные, чтобы видеть свой прогресс обучения

Вступайте в сообщество хендбука

Здесь можно найти единомышленников, экспертов и просто интересных собеседников. А ещё — получить помощь или поделиться знаниями.
Вступить
Сообщить об ошибке
Предыдущий параграф2.3. Ресурсы приложения
Следующий параграф2.5. Коротко о Dart: часть вторая