DartJuniorTechnical

В чём разница между var, dynamic и явными типами в Dart?

var — выведенный статический тип (фиксируется при инициализации), явный тип — статическая аннотация для документации и безопасности, dynamic отключает статическую проверку типов и переносит ошибки в рантайм.

var, dynamic и явные типы в Dart

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

var — выведенный статический тип

При объявлении переменной через var анализатор выводит тип из правой части присваивания. После этого тип зафиксирован и не меняется:

var name = 'Alice';    // тип: String
var count = 42;        // тип: int
var ratio = 3.14;      // тип: double
var items = <int>[];   // тип: List<int>

// name = 123;  // ОШИБКА компиляции: int != String
name = 'Bob'; // OK

// Без инициализации — тип dynamic (не рекомендуется)
var unknown;          // тип: dynamic
unknown = 'text';
unknown = 999;        // OK — dynamic разрешает любое присваивание

Явные типы — документация и защита

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

String firstName = 'Alice';
int age = 30;
List<String> tags = ['dart', 'flutter'];
Map<String, int> scores = {'math': 95, 'science': 88};

// Nullable — явно указывает, что значение может отсутствовать
String? middleName = null;
middleName = 'Marie';

// Параметры функций: всегда явные типы
String greet(String name, {int repeat = 1}) {
    return List.filled(repeat, 'Hello, $name!').join(' ');
}

void main() {
    print(greet('Dart', repeat: 2));
    // Hello, Dart! Hello, Dart!
}

dynamic — отключение статической проверки

dynamic говорит компилятору: «не проверяй обращения к этой переменной». Все проверки перемещаются в рантайм. Это сближает Dart с динамическими языками и открывает путь к NoSuchMethodError:

dynamic value = 'hello';
print(value.length);    // 5 — OK в рантайме

value = 42;
print(value.length);    // RUNTIME ERROR: NoSuchMethodError
                        // int не имеет .length

// dynamic принимает любые вызовы без ошибки компиляции
dynamic x = getSomethingFromJson();
print(x.nonExistentMethod()); // компилируется, падает в рантайме

Object getSomethingFromJson() => 'raw data';

Object vs dynamic

Частая путаница: Object — это тоже базовый тип для всего, но он статически типизирован. Чтобы вызвать метод, нужен каст или проверка:

Object obj = 'hello';
// print(obj.length); // ОШИБКА КОМПИЛЯЦИИ — Object не имеет .length
if (obj is String) {
    print(obj.length); // OK: smart cast внутри блока is
}

dynamic dyn = 'hello';
print(dyn.length);     // компилируется и работает в рантайме

Когда что использовать

  • Явный тип: параметры функций, поля классов, публичное API — всегда явно.
  • var: локальные переменные внутри методов, когда тип очевиден из инициализатора (var user = User()).
  • dynamic: обработка произвольного JSON (Map<String, dynamic>), взаимодействие с отражением (dart:mirrors), FFI-биндинги — и больше почти нигде.
// Типичный паттерн с JSON
Map<String, dynamic> json = {
    'name': 'Alice',
    'age': 30,
    'tags': ['dart', 'flutter'],
};

String name = json['name'] as String;
int age = json['age'] as int;
List<String> tags = List<String>.from(json['tags'] as List);

print('$name, $age, $tags');
// Alice, 30, [dart, flutter]

Подводные камни

  • var без инициализатора становится dynamic: var x; — это dynamic x;. Пишите явный тип или инициализируйте сразу.
  • dynamic «заражает» выражения: результат операций с dynamic тоже dynamic, и ошибки компилятора пропадают цепочкой.
  • Null Safety и dynamic: dynamic неявно включает null как допустимое значение — в отличие от non-nullable типов. Это ломает предположения об отсутствии null.
  • Нет автодополнения: IDE не предлагает методы для dynamic-переменных, что замедляет разработку и скрывает опечатки.
  • Smart cast не работает с dynamic: компилятор не отслеживает суженный тип после if (x is String) для dynamic-переменных в некоторых случаях так же надёжно, как для Object.
  • Приведение типов из JSON небезопасно: json['count'] as int бросит TypeError, если сервер вернул "1" вместо 1. Используйте (json['count'] as num).toInt() или явную конвертацию.
  • final vs var: var подразумевает изменяемость. Предпочитайте final для переменных, которые не переприсваиваются — это явная документация намерений.

Common mistakes

  • Сводить «var, dynamic и явными типами в Dart» к синтаксису и не объяснять Future и Stream.
  • Игнорировать жизненный цикл, основной поток или момент освобождения ресурсов в сценарии dart-2.
  • Выбирать API по привычке, не проверяя состояние, ошибки, доступность и платформенные ограничения.

What the interviewer is testing

  • Формулирует точную модель для «var, dynamic и явными типами в Dart» и подтверждает ее корректным примером.
  • Умеет связать ответ с isolate, тестированием и отладкой на устройстве.
  • Называет ограничения подхода dart-2, включая производительность, память и сопровождение.

Sources

Related topics