Разница между dynamic, Object, var

Зачем столько вариантов?

Dart — это язык со строгой статической типизацией. Это значит, что компилятор знает тип каждой переменной ещё до запуска программы, что помогает избежать множества ошибок.

Но зачем тогда нужны varObject и dynamic?
Они предлагают разный уровень гибкости:

var - умный вывод типов

Ключевое слово var — это не тип. Это инструкция для компилятора:
"Пожалуйста, посмотри на значение, которое я присваиваю, и сам определи тип этой переменной".

Главное правило: как только тип выведен, он фиксируется и не может быть изменён.

Dart - Использование var

Светлая тема Темная тема
// Компилятор понимает что это String
var lang = 'Dart';

// С этого момента переменная lang имеет тип String
print(lang.runtimeType); // Выведет: String

// Попытка присвоить значение другого типа вызовет ошибку компиляции.
lang = 42; 
// ОШИБКА: A value of type 'int' can't be assigned to a variable of type 'String'.

Object - предок всех объектов

Object - это реальный тип данных. Это самый верхний (базовый) класс почти всех типов в Dart.
Любая переменная может быть присвоена типу Object.

Объявив переменную как Object, вы говорите компилятору:
"Я знаю, что здесь лежит какой-то объект, но я не знаю о нём ничего конкретного".

Поэтому можно вызывать только те методы, которые есть у самого класса Object

Dart - Использование Object

Светлая тема Темная тема
Object obj = "Dart on Flutter";
print(w.runtimeType); // Выведет: String

// Но компилятор не знает, что это строка, он видит только Object
// print(w.length); // Ошибка, нет такого свойства у Object

// Даже с проверкой на null, компилятор не даст вызвать метод.
Object? myObject = "Hello";
// print(myObject?.length); // ОШИБКА!

// Чтобы получить доступ к методам строки, нужно явно привести тип.
if (myObject is String) {
  print(myObject.length); // OK, компилятор теперь уверен, что это String.
}

dynamic - отключение проверки типов

Компилятор не будет проводить никаких проверок. Вы можете вызывать любые методы и свойства у dynamic переменной. Вся ответственность за правильность вызовов ложится на вас. Проверка произойдет только в момент выполнения кода (в рантайме).

Dart - Использование dynamic

Светлая тема Темная тема
dynamic lang = 'Dart';
print(lang.length); // 4. Всё в порядке, у строки есть свойство length.

// Тип можно легко поменять. Компилятор не будет возражать.
lang = 42;

// А теперь — самое опасное. Компилятор по-прежнему нам верит.
// Он не видит здесь ошибки!
print(lang.length); // КРАШ В РАНТАЙМЕ!
// Ошибка у целого цисла не свойства length

С большой силой приходит большая ответственность. Отключив проверку типов, вы рискуете получить ошибку в работающем приложении, которую мог бы отловить компилятор.

Классический пример где может приготовится dynamic - это работа с данными в формате JSON, полученными из сети. Структура этих данных не известна на этапе компиляции.

Используйте dynamic только тогда, когда у вас нет другого выбора!

Сравнительная таблица

Характеристика var Object dynamic
Что это? Ключевое слово для вывода типа Базовый тип для всех объектов Маркер, отключающий проверку типов
Проверка типов На этапе компиляции На этапе компиляции На этапе выполнения (runtime)
Изменение типа Нельзя после инициализации Можно, но теряется информация о типе Можно в любой момент
Доступ к методам Доступны все методы выведенного типа Только методы класса Object Доступны любые методы (без гарантий)
Безопасность 🟢 Высокая 🟡 Средняя 🔴 Низкая
Основное назначение Локальные переменные Прием любого объекта с сохранением безопасности Работа с нетипизированными данными (JSON)