В этом (и следующем) параграфе мы будем говорить о 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
в примере выше.
В итоге получаем сразу несколько ошибок до компиляции:
- Переменную
const
нельзя оставить неинициализированной; - Переменную
const
нельзя переопределить; - Переменной
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 есть несколько способов, чтобы создать строку:
- Через одинарные кавычки
'string'
; - Через двойные кавычки
"string"
; - Через тройные кавычки — одинарные
'''string'''
или двойные"""string"""
; - Как «сырую» строку
r'string'
; - Через
StringBuffer
. В основном он нужен для манипуляций со строками.
Общепринятым является первый способ. Однако, это не значит, что другие избыточны. Давайте рассмотрим следующий пример:
На первый взгляд, последний пример с «сырой» строкой (r'Hello, world! I like dollar$$$$’
) может показаться непонятным. Давайте разберёмся.
Например, $
— зарезервированный спецсимвол, и в строках, созданных с помощью кавычек, компилятор будет воспринимать его как символ интерполяции, и в финальную строку не добавит. Чтобы явно сказать компилятору, что в строке нет спецсимволов, нужно использовать сырые строки.
Интерполяция строк
Выше вы могли заметить использование символа $
при выводе значений. Это интерполяция строк — одна из возможностей языка, позволяющих манипулировать со строками.
В Dart есть три способа, чтобы объединить их в одну:
- Конкатенация — склеивание двух строк в одну;
- Интерполяция — встраивание в строку вычисленных значений;
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
и другие.