Интерфейсы

Что такое интерфейс (interface)?

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

Например, интерфейс Printer определяет метод Print(), который может быть реализован разными структурами, такими как User или Document. Для того, чтобы тип реализовывал интерфейс, он должен предоставить определения всех методов интерфейса.

В Go реализация интерфейса происходит неявно, то есть не требуется указывать, что тип реализует интерфейса.

Для чего используется интерфейс?

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

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

Например, интерфейс fmt.Stringer определяет метод String(), который возвращает строковое представление любого типа, реализующего этот интерфейс.

Как в строго типизированным языке сделать функцию, которая работает с разными типами?

Один из способов сделать это - использовать обобщения (generics), которые появились в Go 1.18. Обобщения позволяют объявлять и использовать функции или типы, которые написаны для работы с любым из набора типов, предоставляемых вызывающим кодом. Для этого в Go введены три новых элемента языка:

  • Параметры типов для функций и типов. Это означает, что вы можете задать переменную типа T, которая может принимать разные конкретные типы при вызове функции или создании типа.
  • Определение интерфейсных типов как множеств типов, включая типы, не имеющие методов. Это означает, что вы можете задать ограничение (constraint) на параметр типа, указав, какие свойства должен иметь тип, чтобы его можно было использовать в обобщенной функции или типе.
  • Вывод типов, который позволяет опускать аргументы типов во многих случаях при вызове функции. Это означает, что вы можете не указывать конкретный тип при вызове обобщенной функции, если компилятор может сам его определить из контекста.

Есть интерфейс, а есть указатель на структуру, который nil. Кладем указатель в интерфейс. Что если сравнить интерфейс с nil?

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

Интерфейс, в котором лежит указатель на структуру, и nil интерфейс сравнимы между собой. Если вы сравните интерфейс, в котором лежит указатель на структуру, с nil, результат будет “false”, потому что интерфейс не пустой.

Однако, если вы сравните интерфейс с интерфейсом, содержащим nil указатель, результат будет “false”.

title
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
type SomeInterface interface {
SomeMethod()
}

type SomeStruct struct{}

func (s *SomeStruct) SomeMethod() {}

func main() {
var s SomeStruct
var i1 SomeInterface = &s
var i2 SomeInterface

fmt.Println(i1 == nil) // false
fmt.Println(i2 == nil) // true
}

В примере выше мы создаем интерфейс SomeInterface с методом SomeMethod. Внутри интерфейса i1 лежит указатель на структуру SomeStruct, а интерфейс i2 является пустым интерфейсом. Сравнивая i1 с nil, мы получаем “false”, так как интерфейс i1 не пустой. Сравнивая i2 с nil, мы получаем “true”, так как интерфейс i2 является пустым интерфейсом.

Чем any отличается от пустого интерфейса?

Пустой интерфейс в Go (nil интерфейс) не хранит значение. Если интерфейс пустой, то его сравнение с nil будет всегда возвращать true.

Тип any (тоже известный как интерфейс interface{}) в Go - это специальный тип интерфейса, который может хранить значение любого типа. Тип any является универсальным типом и может быть использован, когда нужно написать обобщенный код, который будет работать с различными типами.

title
1
2
3
4
5
6
7
func main() {
var i1 interface{}
var i2 interface{} = (*SomeStruct)(nil)

fmt.Println(i1 == nil) // true
fmt.Println(i2 == nil) // false
}

Чем пустой интерфейс отличается от пустой структуры?

Пустой интерфейс (interface{}) и пустая структура (struct{}) являются двумя различными понятиями в Go.

Пустая структура (struct{}) не содержит никаких полей и не реализует никаких методов. Она используется в основном для создания переменных, которые не содержат никаких данных. Например:

title
1
var emptyStruct struct{}

Пустой интерфейс (interface{}), с другой стороны, может указывать на любое значение любого типа. Он не имеет никаких методов и не содержит никаких данных. Он используется в основном для передачи значений различных типов в функции или методы. Например:

title
1
2
3
4
var emptyInterface interface{}
emptyInterface = 10
emptyInterface = "hello"
emptyInterface = struct{}{}

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

title
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package main

import (
"fmt"
"math"
)

type Abser interface {
Abs() float64
}

func main() {
var a Abser
f := MyFloat(-math.Sqrt2)
v := Vertex{3, 4}

a = f // a MyFloat implements Abser
a = &v // a *Vertex implements Abser реализует интерфейс

a = v // Ошибка! v - это Vertex (а не *Vertex) и НЕ реализует интерфейс
fmt.Println(a.Abs())
}

type MyFloat float64

func (f MyFloat) Abs() float64 {
if f < 0 {
return float64(-f)
}
return float64(f)
}

type Vertex struct {
X, Y float64
}

func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

Вот вам и interface

Поделиться