---
title: Интерфейсы
seo:
  title: Интерфейсы — Go Developer
  description: "Тема «Интерфейсы» для собеседования Go Developer. Что такое интерфейсы в Go? Чем отличается от интерфейсов в других языках, например, Java, PHP? Что такое утиная типизация?"
---

[Все темы Go Developer](/go-developer)

## <strong>Что такое интерфейсы в Go? Чем отличается от интерфейсов в других языках, например, Java, PHP?</strong> [#q-14bee738d69b81e8a56cc6eed38b0949]

<strong>Интерфейсы</strong> в Go определяют набор методов, которые должен
реализовать тип. Они позволяют создавать гибкий и обобщенный код, не зависящий
от конкретных типов.

#### Основные характеристики&#58;

{/* prettier-ignore */}
1. <strong>Определение интерфейсов</strong>&#58;

    - Интерфейс определяется с помощью ключевого слова <code>type</code> и <code>interface</code>.

    - Пример&#58;

        ```go
        type Speaker interface {
            Speak() string
        }
        ```

1. <strong>Реализация интерфейсов</strong>&#58;

    - Тип автоматически реализует интерфейс, если он содержит все методы, объявленные в интерфейсе.

    - Пример&#58;

        ```go
        type Person struct {
            Name string
        }

        func (p Person) Speak() string {
            return "Hello, my name is " + p.Name
        }
        ```

1. <strong>Использование интерфейсов</strong>&#58;

    - Интерфейсы позволяют работать с любыми типами, которые их реализуют.

    - Пример&#58;

        ```go
        func Greet(s Speaker) {
            fmt.Println(s.Speak())
        }
        ```

#### Отличия от интерфейсов в Java и PHP&#58;

{/* prettier-ignore */}
1. <strong>Неявная реализация</strong>&#58;

    - <strong>Go</strong>&#58; Тип автоматически реализует интерфейс, если он содержит все требуемые методы (не требуется явное указание).

    - Java, PHP&#58; класс явно объявляет реализацию интерфейса с помощью implements.

1. <strong>Без множественного наследования</strong>&#58;

    - <strong>Go</strong>&#58; Поддерживает множественное поведение через композицию интерфейсов, но не поддерживает множественное наследование.

    - <strong>Java</strong>&#58; Поддерживает множественное наследование интерфейсов.

    - <strong>PHP</strong>&#58; Поддерживает множественное наследование интерфейсов.

1. <strong>Интерфейсы и пустой интерфейс</strong>&#58;

    - <strong>Go</strong>&#58; Пустой интерфейс <code>interface&#123;&#125;</code> может содержать значения любого типа.

    - Java и PHP допускают пустые маркерные интерфейсы, но класс должен явно объявить implements. В Go пустому интерфейсу удовлетворяет любой тип.

#### Пример использования интерфейсов в Go&#58;

```go
package main

import "fmt"

type Speaker interface {
    Speak() string
}

type Person struct {
    Name string
}

func (p Person) Speak() string {
    return "Hello, my name is " + p.Name
}

func Greet(s Speaker) {
    fmt.Println(s.Speak())
}

func main() {
    p := Person{Name: "Alice"}
    Greet(p) // Вывод: Hello, my name is Alice
}
```

Интерфейсы в Go обеспечивают гибкость и полиморфизм через неявную реализацию, упрощая разработку и поддержку кода по сравнению с интерфейсами в Java и PHP.

:::note[Ссылки для изучения]

1. [Разбираемся с интерфейсами в Go](https://habr.com/ru/companies/vk/articles/463063/)
   :::

---

## <strong>Что такое утиная типизация?</strong> [#q-14bee738d69b8116ab5de12d687467c0]

<strong>Утиная типизация</strong> — это концепция в программировании, согласно
которой тип объекта определяется его поведением (методами), а не его явным
объявлением.

#### Основные характеристики&#58;

{/* prettier-ignore */}
1. <strong>Неявная реализация</strong>&#58;

    - В Go тип автоматически реализует интерфейс, если он содержит все методы, объявленные в интерфейсе, без явного указания.

    - Пример&#58;

        ```go
        type Speaker interface {
            Speak() string
        }

        type Person struct {
            Name string
        }

        func (p Person) Speak() string {
            return "Hello, my name is " + p.Name
        }
        ```

1. <strong>Гибкость</strong>&#58;

    - Позволяет писать более обобщенный и гибкий код, так как любые типы, имеющие необходимые методы, могут быть использованы там, где ожидается интерфейс.

1. <strong>Происхождение названия</strong>&#58;

    - Название происходит от выражения "If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck." (Если что-то выглядит как утка, плавает как утка и крякает как утка, то, вероятно, это утка).

#### Пример использования утиная типизации в Go&#58;

```go
package main

import "fmt"

type Speaker interface {
    Speak() string
}

type Person struct {
    Name string
}

func (p Person) Speak() string {
    return "Hello, my name is " + p.Name
}

type Dog struct {
    Name string
}

func (d Dog) Speak() string {
    return "Woof! My name is " + d.Name
}

func Greet(s Speaker) {
    fmt.Println(s.Speak())
}

func main() {
    p := Person{Name: "Alice"}
    d := Dog{Name: "Rex"}

    Greet(p) // Вывод: Hello, my name is Alice
    Greet(d) // Вывод: Woof! My name is Rex
}
```

:::note[Ссылки для изучения]

1. [Разбираемся с интерфейсами в Go](https://habr.com/ru/companies/vk/articles/463063/)
   :::

---

## <strong>Внутренее устройство интерфейса, какое оно (структура</strong> <code>iface</code><strong>,</strong> <code>itab</code><strong>)?</strong> [#q-14bee738d69b81ccb5cdd85163cd0d64]

В runtime Go непустой интерфейс обычно представлен парой&#58; указатель на таблицу типа и методов (itab) и данные. Пустой интерфейс хранит указатель на тип и данные без таблицы методов. Приведенные ниже iface и itab — схема реализации, а не стабильный API языка.

{/* prettier-ignore */}
1. <code>iface</code>&#58;

    - <strong>Описание</strong>&#58; Представляет переменную интерфейса и содержит два поля&#58;

        - <code>tab</code> (таблица методов)

        - <code>data</code> (указатель на данные конкретного типа)

    - <strong>Структура</strong>&#58;

        ```go
        type iface struct {
            tab  *itab
            data unsafe.Pointer
        }
        ```

1. <code>itab</code>&#58;

    - <strong>Описание</strong>&#58; Представляет собой таблицу методов для конкретного типа, реализующего интерфейс, и содержит информацию о типе и методах.

    - <strong>Структура</strong>&#58;

        ```go
        type itab struct {
            inter *interfacetype
            _type *_type
            hash  uint32
            _     [4]byte
            fun   [1]uintptr  // массив указателей на функции (методы)
        }
        ```

#### Как это работает&#58;

{/* prettier-ignore */}
1. <strong>Инициализация интерфейса</strong>&#58;

    - Когда переменная интерфейса инициализируется, она получает указатель на <code>itab</code> (таблица методов) и указатель на данные конкретного типа.

1. <strong>Вызов метода</strong>&#58;

    - При вызове метода через интерфейс, Go использует таблицу методов (<code>itab</code>) для вызова соответствующей функции на реальном типе.

#### Пример&#58;

```go
type Speaker interface {
    Speak() string
}

type Person struct {
    Name string
}

func (p Person) Speak() string {
    return "Hello, my name is " + p.Name
}

func main() {
    var s Speaker
    p := Person{Name: "Alice"}
    s = p  // s теперь содержит указатели на itab и данные Person
    fmt.Println(s.Speak())
}
```

Внутреннее устройство интерфейсов в Go с использованием структур <code>iface</code> и <code>itab</code> обеспечивает эффективную реализацию и вызов методов интерфейсов, позволяя типам динамически соответствовать интерфейсам на основе их методов.

:::note[Ссылки для изучения]

1. [Разбираемся с интерфейсами в Go](https://habr.com/ru/companies/vk/articles/463063/)
   :::

---

## <strong>Что такое пустой интерфейс?</strong> [#q-14bee738d69b81838070fb6c46a939eb]

<strong>Пустой интерфейс</strong> (<code>interface&#123;&#125;</code>) — это
интерфейс, который не содержит никаких методов. Он может хранить значение любого
типа.

#### Основные характеристики&#58;

{/* prettier-ignore */}
1. <strong>Определение</strong>&#58;

    - Пустой интерфейс определяется как <code>interface&#123;&#125;</code>.

    - Пример&#58;

        ```go
        var anyValue interface{}
        ```

1. <strong>Гибкость</strong>&#58;

    - Поскольку пустой интерфейс не имеет методов, любой тип удовлетворяет пустому интерфейсу.

1. <strong>Использование</strong>&#58;

    - Применяется для хранения значений различных типов, написания обобщенного кода и работы с неизвестными типами.

#### Пример использования&#58;

```go
package main

import "fmt"

func main() {
    var any interface{}

    any = 42
    fmt.Println(any)  // Вывод: 42

    any = "Hello, Go!"
    fmt.Println(any)  // Вывод: Hello, Go!

    any = true
    fmt.Println(any)  // Вывод: true
}
```

Пустой интерфейс (<code>interface&#123;&#125;</code>) в Go позволяет хранить значения любого типа, что делает его полезным для обобщенного программирования и работы с разнородными данными.

:::note[Ссылки для изучения]

1. [Разбираемся с интерфейсами в Go](https://habr.com/ru/companies/vk/articles/463063/)
   :::

---

## <strong>Что такое</strong> <code>nil интерфейс</code><strong>?</strong> [#q-14bee738d69b81ae95fef5a9a064f216]

<code>Nil интерфейс</code> — это интерфейсная переменная, которая не содержит ни
конкретного типа, ни значения.

#### Основные характеристики&#58;

{/* prettier-ignore */}
1. <strong>Определение</strong>&#58;

    - Интерфейсная переменная инициализируется значением <code>nil</code>, если ей не присвоено никакое значение.

    - Пример&#58;

        ```go
        var any interface{}
        fmt.Println(any == nil)  // Вывод: true
        ```

1. <strong>Поведение</strong>&#58;

    - Nil интерфейс не указывает на конкретный тип и значение.

    - Проверка iface == nil истинна только при отсутствии динамического типа и значения. Если присвоить интерфейсу nil-указатель типа \*T, интерфейс будет ненулевым&#58; он хранит динамический тип \*T.

1. <strong>Использование</strong>&#58;

    - Nil интерфейсы часто используются для проверки наличия значения или для инициализации интерфейсных переменных.

#### Пример использования&#58;

```go
package main

import "fmt"

func main() {
    var any interface{}
    fmt.Println(any == nil)  // Вывод: true

    any = 42
    fmt.Println(any == nil)  // Вывод: false
}
```

:::note[Ссылки для изучения]

1. [Разбираемся с интерфейсами в Go](https://habr.com/ru/companies/vk/articles/463063/)
   :::

---

## <strong>Что такое</strong> <code>type switch</code><strong>?</strong> [#q-14bee738d69b81d4b560df757d4d05ac]

<code>Type switch</code> — это конструкция в Go, которая позволяет выполнять
разные действия в зависимости от конкретного типа значения, хранящегося в
интерфейсной переменной.

#### Основные характеристики&#58;

{/* prettier-ignore */}
1. <strong>Синтаксис</strong>&#58;

    - Похож на обычный <code>switch</code>, но используется для определения типа значения.

    - Пример&#58;

        ```go
        switch v := value.(type) {
        case int:
            fmt.Println("int:", v)
        case string:
            fmt.Println("string:", v)
        case bool:
            fmt.Println("bool:", v)
        default:
            fmt.Println("unknown type")
        }
        ```

1. <strong>Использование</strong>&#58;

    - Полезен для обработки значений различных типов, особенно при работе с пустыми интерфейсами (<code>interface&#123;&#125;</code>).

#### Пример использования&#58;

```go
package main

import "fmt"

func main() {
    var i interface{} = "hello"

    switch v := i.(type) {
    case int:
        fmt.Println("int:", v)
    case string:
        fmt.Println("string:", v)
    case bool:
        fmt.Println("bool:", v)
    default:
        fmt.Println("unknown type")
    }
}
```

:::note[Ссылки для изучения]

1. [Разбираемся с интерфейсами в Go](https://habr.com/ru/companies/vk/articles/463063/)
   :::

---

## <strong>Как определить тип интерфейса?</strong> [#q-14bee738d69b8184bfa8c1ec4f0c8aad]

Для определения типа значения, хранящегося в интерфейсной переменной, используется утверждение типа (type assertion) или конструкция <code>type switch</code>.

#### Утверждение типа (<code>type assertion</code>)&#58;

```go
var i interface{} = 42
v, ok := i.(int)  // Проверка, является ли i значением типа int
if ok {
    fmt.Println("int:", v)
} else {
    fmt.Println("not an int")
}
```

#### Использование <code>type switch</code>&#58;

```go
var i interface{} = "hello"

switch v := i.(type) {
case int:
    fmt.Println("int:", v)
case string:
    fmt.Println("string:", v)
default:
    fmt.Println("unknown type")
}
```

Эти методы позволяют определить и обработать тип значения, хранящегося в интерфейсной переменной.

:::note[Ссылки для изучения]

1. [Разбираемся с интерфейсами в Go](https://habr.com/ru/companies/vk/articles/463063/)
   :::

---

## <strong>Как преобразовать интерфейс к другому типу?</strong> [#q-14bee738d69b81829220fd12e7009f12]

Для преобразования интерфейса к другому типу используется утверждение типа (<code>type assertion</code>).

#### Утверждение типа (<code>type assertion</code>)&#58;

- <strong>Синтаксис</strong>&#58; <code>value&#46;&#40;Type&#41;</code>

- <strong>Использование</strong>&#58; Позволяет преобразовать значение
  интерфейса к конкретному типу.

#### Пример&#58;

```go
package main

import "fmt"

func main() {
    var i interface{} = 42  // Интерфейсное значение

    // Преобразование интерфейса к типу int
    v, ok := i.(int)
    if ok {
        fmt.Println("int:", v)  // Вывод: int: 42
    } else {
        fmt.Println("Not an int")
    }
}
```

#### Особенности&#58;

{/* prettier-ignore */}
1. <strong>Проверка успешности</strong>&#58;

    - Утверждение типа возвращает два значения&#58; преобразованное значение и булевый флаг успешности.

    - Если преобразование успешно, <code>ok</code> будет <code>true</code>.

1. <strong>Паника при неудаче</strong>&#58;

    - Если не использовать проверку успешности, неудачное преобразование вызовет панику.

    - Пример&#58;

        ```go
        v := i.(int)  // Паника, если i не является int
        ```

:::note[Ссылки для изучения]

1. [Разбираемся с интерфейсами в Go](https://habr.com/ru/companies/vk/articles/463063/)
   :::

---

## <strong>Где следует поместить описание интерфейса&#58; в пакете с реализацией или в пакете, где этот интерфейс используется? Почему?</strong> [#q-14bee738d69b813e87fcfcb394d38d5a]

<strong>Описание интерфейса</strong> следует поместить в пакет, где этот
интерфейс используется, а не в пакете с реализацией.

#### Почему&#58;

{/* prettier-ignore */}
1. <strong>Обратная зависимость</strong>&#58;

    - Избегание циклических зависимостей и поддержка слабой связи между пакетами.

    - Реализации могут находиться в разных пакетах, не требуя изменения интерфейса.

1. <strong>Интерфейсы как контракты</strong>&#58;

    - Интерфейсы определяют контракты, которым должны соответствовать реализации, а не наоборот.

    - Пакет, использующий интерфейс, определяет требования и поведение, ожидаемое от реализаций.

#### Пример&#58;

```go
// Пакет user
package user

type Speaker interface {
    Speak() string
}
```

```go
// Пакет main
package main

import "user"

type Person struct {
    Name string
}

func (p Person) Speak() string {
    return "Hello, my name is " + p.Name
}

func main() {
    var s user.Speaker
    s = Person{Name: "Alice"}
    fmt.Println(s.Speak())
}
```

Этот подход обеспечивает лучшую модульность и повторное использование кода.

:::note[Ссылки для изучения]

1. [Разбираемся с интерфейсами в Go](https://habr.com/ru/companies/vk/articles/463063/)
   :::
