Пустой интерфейс (any)
Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy — урок 28 из 107.
Пустой интерфейс — это интерфейс с нулевым количеством методов. Поскольку каждый тип в Go имеет как минимум ноль методов, любой тип автоматически удовлетворяет пустому интерфейсу. Это позволяет ему хранить значения любого типа.
var anything interface{}
anything = 42
anything = "hello"
anything = true
anything = []int{1, 2, 3}Начиная с Go 1.18, ключевое слово any было введено как псевдоним для interface{}. Они полностью взаимозаменяемы, но any читается легче.
var value any = "Go is awesome"Пустой интерфейс полезен, когда вам нужно работать со значениями неизвестных или различных типов. Функции, такие как fmt.Println, используют его для приема аргументов любого типа:
func PrintAnything(v any) {
fmt.Println("Value:", v)
}
func main() {
PrintAnything(100)
PrintAnything("text")
PrintAnything(3.14)
}Однако здесь есть компромисс. Когда значение сохраняется в пустом интерфейсе, вы теряете доступ к информации о его конкретном типе.
Вы не можете напрямую вызывать методы или обращаться к полям, не извлекая предварительно базовый тип. В следующих уроках будет рассмотрено, как извлечь конкретный тип из пустого интерфейса с помощью утверждений типа (type assertions) и переключателей типов (type switches).
Задание
ЛегкоДавайте создадим гибкий контейнер для хранения, который может содержать значения любого типа, используя пустой интерфейс. Вы создадите простую коробку, которая демонстрирует, как any позволяет хранить разные типы в одной и той же структуре.
Вы организуете свой код в двух файлах:
box.go: Определите структуруBoxс единственным полемContentтипаany. Создайте функцию-конструкторNewBox, которая принимает значение любого типа и возвращает указатель на Box, содержащий это значение. Добавьте методDescribe, который возвращает строку, показывающую, что хранится в коробке.main.go: Считайте различные значения из входных данных, создайте коробки, содержащие целое число, строку и логическое значение, затем выведите описание каждой коробки, чтобы показать, как один и тот же тип Box может содержать совершенно разные значения.
Будут предоставлены следующие входные данные:
- Строка 1: Целочисленное значение
- Строка 2: Строковое значение
- Строка 3: Логическое значение (
trueилиfalse)
Ваш метод Describe должен возвращать строку в следующем формате:
Box contains: [value]Например, для входных данных 42, hello и true ваш вывод должен быть следующим:
Box contains: 42
Box contains: hello
Box contains: trueКлючевая идея здесь заключается в том, что ваша единственная структура Box может хранить целое число, строку или логическое значение — и все это благодаря тому, что тип any принимает значения любого типа. Каждая коробка содержит что-то свое, но при этом все они относятся к одному и тому же типу Box.
Шпаргалка
Пустой интерфейс (interface{}) — это интерфейс с нулем методов. Поскольку каждый тип в Go имеет как минимум ноль методов, каждый тип автоматически удовлетворяет пустому интерфейсу, что позволяет ему хранить значения любого типа.
var anything interface{}
anything = 42
anything = "hello"
anything = true
anything = []int{1, 2, 3}Начиная с Go 1.18, ключевое слово any было введено как псевдоним для interface{}. Они полностью взаимозаменяемы, но any легче читать:
var value any = "Go is awesome"Пустой интерфейс полезен для работы со значениями неизвестных или различных типов. Функции могут принимать аргументы любого типа:
func PrintAnything(v any) {
fmt.Println("Value:", v)
}
func main() {
PrintAnything(100)
PrintAnything("text")
PrintAnything(3.14)
}Компромисс: Когда значение хранится в пустом интерфейсе, вы теряете доступ к информации о его конкретном типе. Вы не можете напрямую вызывать методы или обращаться к полям, не извлекая сначала базовый тип с помощью утверждений типа (type assertions) или переключателей типов (type switches).
Попробуйте сами
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Чтение целочисленного значения
line1, _ := reader.ReadString('\n')
intVal, _ := strconv.Atoi(strings.TrimSpace(line1))
// Чтение строкового значения
line2, _ := reader.ReadString('\n')
strVal := strings.TrimSpace(line2)
// Чтение логического значения
line3, _ := reader.ReadString('\n')
boolVal, _ := strconv.ParseBool(strings.TrimSpace(line3))
// TODO: Создайте контейнеры для каждого значения с помощью NewBox
// TODO: Выведите описание каждого контейнера с помощью метода Describe
// Используйте переменные, чтобы избежать ошибок компиляции (удалите эти строки при реализации)
_ = intVal
_ = strVal
_ = boolVal
// Пример формата вывода:
// fmt.Println(box.Describe())
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП в Go
Внешние файлыРабочее пространство и модули GoПакеты и импортыЭкспортируемые и неэкспортируемые именаВведение в ООП в GoСтруктуры как классыОпределение методов структурПолучатели-указатели и получатели-значенияИнициализация структурФункции-конструкторыИтоги — Простой калькулятор4Интерфейсы
Введение в интерфейсыНеявная реализацияИнтерфейс как контрактПустой интерфейс (any)Утверждение типаПереключатель типовКомпозиция интерфейсовИнтерфейсы Stringer и ErrorПовторение: Калькулятор фигур7Инкапсуляция
Экспортируемые и неэкспортируемые поляИнкапсуляция на уровне пакетовГеттеры и сеттерыСокрытие информации в GoИтоги — Записи о студентах10Обобщения (Generics) (Go 1.18+)
Введение в GenericsПараметры типовОграничения типовОбобщенные структурыОбходной путь для обобщенных методовИтоги — Обобщенная коллекция13Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн Abstract FactoryПаттерн ObserverПаттерн Strategy2Глубокое погружение в типы и структуры
Базовые и составные типыОпределение пользовательских типовТеги структурАнонимные структурыВложенные структурыНулевые значения и значения по умолчаниюПовторение — Контактная книга5Композиция вместо наследования
Почему в Go нет наследованияОсновы встраивания структурПродвижение методовВстраивание нескольких структурВстраивание против агрегацииЗатенение встроенных методовИтоги — Иерархия сотрудников8Обработка ошибок и ООП
Интерфейс errorПользовательские типы ошибокОбертывание ошибок (fmt.Errorf)Sentinel-ошибкиerrors.Is() и errors.As()Panic, Defer и RecoverИтоги — Парсер файлов11Стандартная библиотека и ООП
io.Reader и io.Writersort.InterfaceИнтерфейс fmt.Stringerencoding/json со структурамиИнтерфейс http.HandlerПовторение: модели REST API14Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн КомпоновщикMiddleware как Декоратор3Указатели и память
Основы указателей в GoУказатели на структурыПередача по значению и по ссылкеФункция new()Сборка мусора в GoПовторение: Конструктор связного списка6Полиморфизм в Go
Полиморфизм через интерфейсыУтиная типизация в GoПравила реализации интерфейсовПолиморфные коллекцииВнедрение зависимостейИтоги — Обработчик платежей9Конкурентность и ООП
Основы горутинКаналы и взаимодействиеБуферизованные и небуферизованные каналыОператор selectsync.Mutex и sync.RWMutexsync.WaitGroupПроектирование потокобезопасных структурПовторение — Worker Pool