Интерфейсы в Go
Теория: Пустой интерфейс
В Go есть специальный тип — пустой интерфейс, записываемый как interface{}. Он не содержит методов, поэтому любой тип автоматически реализует этот интерфейс. Это значит, что переменная типа interface{} может хранить значение любого типа: число, строку, структуру, функцию и так далее. Начиная с версии Go 1.18, для удобства появился псевдоним any, который полностью равнозначен interface{}.
Пустой интерфейс используется, когда заранее неизвестен тип значения, которое будет передаваться в функцию или храниться в структуре. Примером может служить функция вывода на экран, которая принимает любые значения:
В этом примере функция принимает interface{}, что позволяет передавать туда значения любых типов. Аналогичная запись с any будет выглядеть так:
В коде разницы между interface{} и any нет, это просто два имени одного и того же типа.
Однако, чтобы работать с данными внутри пустого интерфейса, нужно привести его к конкретному типу. Это делается через утверждение типа:
Если тип внутри interface{} не совпадает с ожидаемым, программа аварийно завершится. Поэтому при работе с пустым интерфейсом важно проверять типы, например, через type switch:
Пустой интерфейс широко используется для создания универсальных функций. Универсальные функции — это функции, которые работают с разными типами данных, не привязываясь к конкретному типу. Например, функция, принимающая любое количество аргументов любых типов:
Ещё пример — функция фильтрации среза из значений разных типов:
Хотя такие функции гибкие, у них есть недостаток — отсутствие статической проверки типов. Это приводит к необходимости проверять типы вручную и риску ошибок в рантайме.
С выходом Go 1.18 появилась возможность писать обобщённый код с помощью параметров типов (generics). Обобщения позволяют создавать универсальные функции и типы с сохранением безопасности типов на этапе компиляции.
Вот пример универсальной функции вывода:
Параметр типа T обозначает произвольный тип. Ключевое слово any — это ограничение, которое говорит, что T может быть любым типом.
Пример универсальной функции фильтрации:
Обобщения удобны и безопасны, но работают с однородными типами. Для разнородных коллекций или данных с динамической структурой по-прежнему может понадобиться использовать interface{}.


