Интерфейс - это подпись / контракт, которому могут соответствовать другие типы.
Подпись может включать одну или несколько сигнатур методов, что означает имена методов, аргументы (включая типы) и возвращаемые аргументы.Если он не включает какой-либо метод, это на самом деле печально известный тип interface{}
, которому соответствует каждый тип.
Чтобы соответствовать интерфейсу, тип должен строго реализовывать полную сигнатуру, включая все переданные и возвращенныеаргументы и их типы.
Интерфейсы и структуры имеют разные типы.
Поэтому в следующем примере Type2
не реализует Intf2
:
type Intf1 interface{
Bar()
}
type Type1 struct {}
func (s SomeType) Bar() {}
type Intf2 interface{
Foo() Intf1
}
// Type2 does not implement Intf2
type Type2 struct {}
func (s Type2) Foo() Type1 {}
// Won't compile
var _ Intf2 = Type2{}
Поскольку компилятор go учитываетПодпись метода Foo
на Intf2
будет другой, поскольку тип возвращаемого значения отличается.Компилятор не делает вывод, что возвращаемый тип также реализует интерфейс, потому что это принесет большую сложность для этого.
Если вы хотите, чтобы этот пример работал, вам нужно изменить Type2
на следующее:
// Type2 does not implement Intf2
type Type2 struct {}
func (s Type2) Foo() Intf1 {}
Это также относится к переданным аргументам, а не только к возвращаемым аргументам.
Теперь, что касается циклических зависимостей, я предлагаю вам представить свои интерфейсы в третьем пакете, который действует как клей и пакет верхнего уровня.Одна из распространенных вещей - это иметь основной пакет, который объединяется с интерфейсами для достижения своей основной цели.
Пример:
pkg config
type Source interface{}
type Parser interface{}
pkg parsers
pkg jsonparser
implements config.Parser
pkg yamlparser
implements config.Parser
pkg sources
pkg filesource
implements config.Source
```