В чем разница между dynamic, var и Object в Dart?

Ответ

Эти три ключевых слова служат разным целям в системе типов Dart, и их часто путают.

Ключевое слово Статический тип Изменение типа Проверка типов Основное назначение
var Выводится при инициализации Невозможно На этапе компиляции Укорочение записи для локальных переменных с очевидным типом.
dynamic dynamic (специальный тип) Возможно На этапе выполнения (отложена) Работа с динамическим контентом (JSON, плагины), когда тип неизвестен на этапе компиляции.
Object? Object? (базовый nullable-тип) Невозможно (но можно присвоить любой объект) На этапе компиляции Хранение любого объекта с обязательной проверкой или приведением типа перед использованием его специфичных методов.

Детальное сравнение с примерами:

  1. var — вывод типа:

    var message = 'Привет'; // Статический тип выведен как `String`
    // message = 123; // ОШИБКА компиляции: тип `int` не присваивается `String`.
    print(message.length); // OK, компилятор знает, что это String.
  2. dynamic — отключение статической проверки:

    dynamic item = 'Текст';
    print(item.length); // OK, работает (item — String).
    
    item = 100; // Теперь item содержит `int`.
    print(item.length); // ОШИБКА ВО ВРЕМЯ ВЫПОЛНЕНИЯ (Runtime Error)! 
    // Компилятор пропустит это, но при запуске у int нет свойства .length.

    dynamic следует использовать осознанно, только когда тип действительно неизвестен (например, данные из json.decode()).

  3. Object? — тип верхнего уровня:

    Object? something = 'Строка';
    something = 45; // OK, int — это тоже Object.
    something = null; // OK, Object? — nullable.
    
    // print(something.length); // ОШИБКА компиляции!
    // Компилятор не знает, что something — String, даже если там строка.
    
    // Требуется явная проверка или приведение типа:
    if (something is String) {
      print(something.length); // OK, в этой ветке тип продвинут до String.
    }
    // ИЛИ
    print((something as String).length); // Runtime error, если something не String.

Рекомендация по использованию:

  • Предпочитайте var для локальных переменных, где тип ясен.
  • Используйте явные типы (String, List<int>) для публичных API (полей классов, аргументов и возвращаемых значений методов) для лучшей документированности.
  • Object? используйте, когда вам нужна коллекция или переменная, гарантированно способная хранить любой объект, но с последующей безопасной проверкой типа (is).
  • dynamic — инструмент на крайний случай. Его использование должно быть ограничено сценариями, где статическая типизация невозможна.

Ответ 18+ 🔞

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

Вот смотри, есть три штуки: var, dynamic и Object?. Понимаешь, в чём подвох? Все они как бы могут хранить что угодно, но ведут себя потом — просто пиздец по-разному. Это как три чувака в баре: один точно знает, что он пьёт, второй хлещет всё подряд, а третий — такой хитрая жопа, который вроде и всё может налить, но перед глотком обязательно понюхает.

Краткая выжимка, чтобы не охуеть:

Ключевое слово Что за тип? Менять можно? Кто проверяет? Зачем это вообще?
var Угадывается при создании Нет, ёпта Компилятор, сразу Чтобы не писать длинные типы, когда и так всё ясно.
dynamic dynamic (тип-похуй) Да, запросто Программа, когда уже запустилась Для работы с полной неизвестностью (типа JSON). Опасно, блядь.
Object? Object? (праотец всего) Нет, но можно любой объект сунуть Компилятор, но он придирается Чтобы хранить что угодно, но потом самому проверить, что за зверь в клетке.

А теперь подробнее, с примерами, чтобы волнение ебать ушло:

  1. var — умный дядя. Ты ему один раз показываешь, что в руке, а он запоминает навсегда. Попробуй подсунуть другое — сразу вмандит ошибку компиляции.

    var greeting = 'Ну привет'; // Компилятор видит: а, `String`.
    // greeting = 42; // ОШИБКА! Ты чё, мудила? Тут же строка должна быть!
    print(greeting.length); // Всё ок, он знает, что у строк есть длина.

    Используй его, когда лень писать String greeting = .... Удобно и безопасно.

  2. dynamic — отбитый на голову авантюрист. Сказал ему «держи» — он взял. Сказал «держи другое» — он взял и это. А потом пытаешься с этим что-то сделать, и получаешь ошибку прямо в лицо во время работы программы. Доверия к нему — ноль ебать.

    dynamic stuff = 'Текст';
    print(stuff.length); // Ну, ок, работает. Строка же.
    
    stuff = 100; // А теперь тут число. dynamic сказал: «Да похуй, клади».
    print(stuff.length); // БАБАХ! ОШИБКА ВО ВРЕМЯ ВЫПОЛНЕНИЯ!
    // Компилятор промолчал, но когда программа запустилась, оказалось, что у числа нет `.length`. Вот и всё, приехали.

    Это как дать слепому рулить. Используй только в крайних случаях, когда реально нихуя не понятно, что прилетит.

  3. Object? — осторожный бухгалтер. Может принять любой объект (и даже null), но он не дурак. Прежде чем что-то с этим делать, ты должен доказать ему, что ты знаешь, что делаешь.

    Object? something = 'Опять строка';
    something = 45; // Можно, 45 — это объект.
    something = null; // И это можно, `?` же есть.
    
    // print(something.length); // ОШИБКА компиляции сразу!
    // Компилятор: «Слышь, пацан, я не уверен, что там строка. Докажи».
    
    // Доказываешь проверкой:
    if (something is String) { // «Э, бошка, думай!» — проверяешь тип.
      print(something.length); // Теперь тут всё ясно, можно работать.
    }
    // Или грубо, через приведение (может взорваться, как и dynamic):
    // print((something as String).length); // Упадет, если something не String.

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

Итог, чувак:

  • var — твой основной инструмент для локальных переменных. Быстро и надёжно.
  • Конкретные типы (String, List<int>) — для всего публичного (поля, аргументы методов). Чтобы другие не охуели, пытаясь понять, что тут должно быть.
  • Object? — когда нужна коробка для всего на свете, но ты готов потом копаться в ней с фонариком (is).
  • dynamic — это красная кнопка. Жмёшь только когда другого выхода нет, и готов нести ответственность за возможный взрыв на этапе выполнения.