Это называется «типизацией утки» и позволяет определять интерфейсы там, где они необходимы, а не как часть самого типа данных. Рассмотрим следующий тип:
type X struct {...}
func (X) f()
func (X) g()
func (X) h()
Тип X
имеет три метода: f()
, g()
, h()
. Если у вас есть структура данных или функция, которая должна вызывать метод f()
, вы можете определить интерфейс:
type FIntf interface {
f()
}
, и теперь X
реализует этот интерфейс. Вы можете передавать экземпляры X
везде, где требуется FIntf
.
Если в другом модуле вам нужны g()
и h()
, вы можете определить интерфейс там:
type GIntf interface {
g()
h()
и теперь X
реализует GIntf
.
Это особенно полезно, если у вас есть сторонняя библиотека, которая не реализует необходимые вам интерфейсы. Вы можете просто определить интерфейс, в котором вы его используете, и использовать сторонние типы с правильным набором методов для реализации вашего интерфейса.
Основное преимущество этого метода заключается в том, что вы все еще можете эмулировать традиционное понятие интерфейсов. где вы определяете интерфейс и конкретную реализацию этого. Кроме того, у вас есть возможность определять различные интерфейсы по мере необходимости, не меняя реализацию. На языке, подобном Java, если у вас есть функция, которая получает определенный интерфейс, а если у вашего объекта нет, вы должны написать адаптер, даже если набор методов существует в исходном типе. В Go вам не нужно этого делать.
Утиная печать также обеспечивает безопасность типов при вызове методов. Например, если у вас есть функция, которая должна вызывать метод x()
и y()
одного из своих аргументов, определите интерфейс, содержащий x()
и y()
, и используйте утверждение типа для проверки аргумента, реализует эти два методы.