Определение и реализация интерфейсов
Неявная реализация и пустой интерфейс
«Определение и реализация интерфейсов» — бесплатный урок Go Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Go Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Go Academy содержит 4 уроков всего.
Что такое интерфейс
Интерфейс в Go — это набор сигнатур методов. Любой тип, реализующий все эти методы, неявно соответствует интерфейсу — ключевое слово implements не требуется. Это называется структурной типизацией или утиной типизацией.
Объявление интерфейса
Определите интерфейс с помощью ключевого слова type:
package main
import "fmt"
type Shape interface {
Area() float64
Perimeter() float64
}
type Circle struct{ Radius float64 }
func (c Circle) Area() float64 { return 3.14159 * c.Radius * c.Radius }
func (c Circle) Perimeter() float64 { return 2 * 3.14159 * c.Radius }
func main() {
var s Shape = Circle{5}
fmt.Printf("Area: %.2f\n", s.Area())
}Неявное соответствие
Объявлять о намерении не требуется. Если у типа есть подходящие методы, он автоматически соответствует интерфейсу:
package main
import "fmt"
type Greeter interface{ Greet() string }
type English struct{}
func (e English) Greet() string { return "Hello" }
type Spanish struct{}
func (s Spanish) Greet() string { return "Hola" }
func say(g Greeter) { fmt.Println(g.Greet()) }
func main() {
say(English{})
say(Spanish{})
}Пустой интерфейс
interface{} (или any в Go 1.18 и более поздних версиях) поддерживается каждым типом. Он используется, когда тип неизвестен во время компиляции:
package main
import "fmt"
func printAny(v any) { fmt.Printf("%T: %v\n", v, v) }
func main() {
printAny(42)
printAny("hello")
printAny([]int{1,2,3})
}Значения интерфейсов
Значение интерфейса содержит две вещи: конкретный тип и конкретное значение. У нулевого интерфейса оба компонента имеют нулевое значение:
package main
import "fmt"
type Animal interface{ Sound() string }
type Dog struct{}
func (d Dog) Sound() string { return "woof" }
func main() {
var a Animal
fmt.Println(a == nil) // true
a = Dog{}
fmt.Println(a == nil) // false
fmt.Println(a.Sound())
}Соответствие нескольким интерфейсам
Тип может одновременно соответствовать нескольким интерфейсам:
package main
import "fmt"
type Reader interface{ Read() string }
type Writer interface{ Write(string) }
type ReadWriter interface{ Reader; Writer }
type File struct{ content string }
func (f *File) Read() string { return f.content }
func (f *File) Write(s string) { f.content = s }
func main() {
var rw ReadWriter = &File{}
rw.Write("hello")
fmt.Println(rw.Read())
}Присваивание интерфейсов
Более общий интерфейс может содержать более узкое конкретное значение. Для присваивания между типами интерфейсов конкретный тип должен соответствовать обоим интерфейсам:
package main
import "fmt"
type Stringer interface{ String() string }
type Point struct{ X, Y int }
func (p Point) String() string { return fmt.Sprintf("(%d,%d)", p.X, p.Y) }
func main() {
var s Stringer = Point{3, 4}
fmt.Println(s.String())
}Ловушка нулевого интерфейса
Интерфейс, не равный нулю и содержащий нулевое конкретное значение, не равен нулю (NOT):
package main
import "fmt"
type MyErr struct{}
func (e *MyErr) Error() string { return "oops" }
func bad() error {
var e *MyErr // nil pointer
return e // returns non-nil interface!
}
func main() {
err := bad()
fmt.Println(err == nil) // false — common gotcha
}Интерфейсы упрощают тестирование
Определение зависимостей как интерфейсов позволяет заменять настоящие реализации заглушками в тестах:
package main
import "fmt"
type EmailSender interface{ Send(to, body string) error }
type MockSender struct{ Sent []string }
func (m *MockSender) Send(to, body string) error {
m.Sent = append(m.Sent, to)
return nil
}
func notify(s EmailSender, addr string) { s.Send(addr, "hello") }
func main() {
mock := &MockSender{}
notify(mock, "a@b.com")
fmt.Println(mock.Sent) // [a@b.com]
}Принимайте интерфейсы, возвращайте структуры
Рекомендуемый подход в Go: параметры функций должны быть интерфейсами (для гибкости), а возвращаемые типы — конкретными типами (чтобы вызывающим сторонам было ясно, что именно они получают):
package main
type Logger interface{ Log(string) }
type FileLogger struct{}
func (f *FileLogger) Log(s string) {}
func NewFileLogger() *FileLogger { // returns concrete
return &FileLogger{}
}
func process(l Logger) {} // accepts interface
func main() { process(NewFileLogger()) }Делайте интерфейсы небольшими
Стандартная библиотека Go отдаёт предпочтение крошечным интерфейсам: у io.Reader один метод, у io.Writer один метод. Небольшие интерфейсы проще комбинировать и тестировать с заглушками.
Быстрая проверка
Нужно ли типу объявлять, что он реализует интерфейс?
Итоги: интерфейсы
Главные выводы:
- Интерфейсы определяют поведение, а не данные
- Соответствие является неявным — ключевое слово
implementsне требуется - Пустой интерфейс (
any) принимает любой тип - Помните о ловушке нулевого интерфейса
- Делайте интерфейсы небольшими и специализированными
Практическое задание
Определите интерфейс Storage с методами Save(key, value string) error и Load(key string) (string, error). Реализуйте его с помощью MemStorage на основе отображения и протестируйте его.
Часто задаваемые вопросы
Урок «Определение и реализация интерфейсов» бесплатный?
Да — полный текст урока «Определение и реализация интерфейсов» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Go Academy, подпишись на CoddyKit PRO. Курс Go Academy содержит 4 уроков всего.
Чему я научусь в уроке «Определение и реализация интерфейсов»?
Неявная реализация и пустой интерфейс Ты практикуешь Go Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Go Academy?
Предыдущий опыт не требуется. Go Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «Определение и реализация интерфейсов»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Go Academy?
Да. Каждый урок Go Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Определение и реализация интерфейсов
- Интерфейсы основной библиотеки
- Утверждения типов и переключатели типов
- Композиция интерфейсов и лучшие практики