Что это значит в Golang, когда интерфейс используется внутри структуры?

#go #interface

#Вперед #интерфейс

Вопрос:

Новое для перехода сюда. Я наткнулся на некоторый код, который имеет следующее отношение к DynamoDB:

 type Dynamo interface {
    DescribeTableWithContext(
        aws.Context,
        *dynamodb.DescribeTableInput,
        ...request.Option,
    ) (*dynamodb.DescribeTableOutput, error)
}

type my_struct struct {
    Dynamo
}
  

Правильно ли я предполагаю, что my_struct «реализует» интерфейс Dynamo и теперь может использовать DescribeTableWithContext метод?

Комментарии:

1. Больше похоже, что он только удовлетворяет интерфейсу, однако попытка вызвать метод в экземпляре my_struct без реальной, конкретной реализации завершится неудачей во время выполнения .

2. Это означает то же самое, что если бы внутри находился неинтерфейсный тип: это встраивание и работает как обычное встраивание, т. Е. у вас есть безымянное поле этого типа, и, как и в случае с типами указателей или карт, вы должны инициализировать его, прежде чем оно станет пригодным для использования.

Ответ №1:

Прав ли я, предполагая, что my_struct «реализует» интерфейс Dynamo

Не совсем. Какая бы структура, которую вы ни инициализировали my_struct для Dynamo внедрения, будет тем, что реализует интерфейс. my_struct однако будет удовлетворять Dynamo интерфейсу во время компиляции. Однако, как указывает @mkopriva, во время выполнения это требует конкретной реализации встроенного интерфейса. Итак, если бы вы должны были сделать что-то вроде этого:

 package main

import "fmt"

type Adder interface {
    func Add(a, b int) int
}

type Embed struct {
    Adder
}

func PrintAdd(a Adder, first, second int) {
    fmt.Println(a.Add(first, second))
}

func main() {
    e := Embed{}
    PrintAdd(e, 1, 2)
}
  

Этот код будет скомпилирован, но во время выполнения вызов PrintAdd завершится неудачей, поскольку реализация встроенного интерфейса не была установлена.

Если вы замените вышеупомянутый main на:

 type adder struct {}

func (a adder) Add(first, second int) int {
     return first   second
}

func main() {
    e := Embed{adder{}}
    PrintAdd(e, 1, 2)
}
  

Все будет работать так, как ожидалось.

… и теперь можете использовать метод DescribeTableWithContext?

Да, предполагая, что вы предоставили реализацию интерфейса во время инициализации.

РЕДАКТИРОВАТЬ: Добавлено объяснение того, что значит реализовать интерфейс, а не просто удовлетворять ему.

Комментарии:

1. Ваше начальное утверждение неверно. Встраивание типа интерфейса в определение структуры не означает, что структура реализует интерфейс. Для вызывающих будет казаться, что это так, но вызовы (в вашем примере) Add on a действительно будут ретранслироваться в экземпляр, adder назначенный a.Adder . Если бы Embed нужно было реализовать Add себя, то вместо этого был бы вызван этот метод (переопределяющий), учитывая, что вызывающий объект явно не обходит его вызовом a.Adder.Add .

2. Просто обновил свой ответ, чтобы быть более конкретным в отношении того, что значит удовлетворять, а не реализовывать интерфейс. Справедливое замечание, спасибо за ответ.

Ответ №2:

Прав ли я, предполагая, что my_struct»реализует» интерфейс Dynamo и теперь может использовать метод DescribeTableWithContext?

Нет. Что это делает, так это встраивает неназванное поле типа Dynamo в вашу структуру. Вы сможете вызывать DescribeTableWithContext непосредственно экземпляры my_struct (например, var my_struct_instance my_struct ), но этот вызов фактически вызовет реализацию того, что вы назначили my_struct_instance.Dynamo , а не реализацию внутри my_struct (потому что это не определяет DescribeTableWithContext и, следовательно, не реализует Dynamo ). Выполнение этого может пройти успешно (т. Е. без паники) только после установки my_struct_instance.Dynamo значения (или указателя на значение) с типом, который реализует Dynamo . Реализация интерфейсов в Go выполняется с помощью утиного ввода.