Неинтерфейсные методы в реализации интерфейса
У меня есть интерфейс, который определяет метод. У меня есть структура, которая реализует этот интерфейс. В нем я реализовал методы из этого интерфейса, а также определил дополнительные методы.
Например:
package main
import (
"fmt"
)
type Animal interface {
MakeNoise()
}
type Dog struct {
color string
}
/* Interface implementation */
func (d *Dog) MakeNoise() {
fmt.Println("Bark!")
}
/* End Interface implementation */
func (d *Dog) WagTail() {
fmt.Println(d.color + " dog: Wag wag")
}
func NewDog(color string) Animal {
return &Dog{color}
}
func main() {
dog := NewDog("Brown")
dog.MakeNoise()
dog.WagTail()
}
На детской площадке: https://play.golang.org/p/B1GgoNToNl_l
Здесь WagTail() не является частью интерфейса Animal, но принадлежит структуре Dog. Запуск этого кода дает ошибку
dog.WagTail undefined (тип Animal не имеет поля или метода WagTail).
Есть ли способ, которым я мог бы иметь структуру придерживаться интерфейса, а также определить его собственные методы?
3 ответа
Ошибка описала все это:
dog.WagTail undefined (тип Animal не имеет поля или метода WagTail)
Для реализации интерфейса вы должны реализовать все методы, определенные внутри него.
dog := NewDog("Brown")
dog.MakeNoise()
dog.WagTail()
Сейчас NewDog
возвращает интерфейс Animal, который содержит MakeNoise
метод но не WagTail
,
Единственный способ управлять вашим требованием - создать переменную типа структуры Dog
и тогда вы можете вызвать любой метод, имеющий Dog в качестве получателя.
d := &Dog{"Brown"}
d.WagTail()
Или вы можете вернуть указатель на Dog
структура из NewDog
метод, как вы сделали в своем коде, упомянутом в комментарии как:
func NewDog(color string) *Dog {
return &Dog{color}
}
Но если метод не определен в интерфейсе, вы не сможете реализовать его, используя структуру как приемник метода.
Голанг обеспечивает способ, которым:
Вы можете попросить компилятор проверить, что тип T реализует интерфейс I, пытаясь присвоить, используя нулевое значение для T или указатель на T, в зависимости от ситуации.
type T struct{}
var _ I = T{} // Verify that T implements I.
var _ I = (*T)(nil) // Verify that *T implements I.
Если T (или *T, соответственно) не реализует I, ошибка будет обнаружена во время компиляции.
Если вы хотите, чтобы пользователи интерфейса явно объявили, что они реализуют его, вы можете добавить метод с описательным именем к набору методов интерфейса. Например:
type Fooer interface {
Foo()
ImplementsFooer()
}
Затем тип должен реализовать метод ImplementsFooer, чтобы быть Fooer, четко документируя факт и объявляя его в выводе godoc.
type Bar struct{}
func (b Bar) ImplementsFooer() {}
func (b Bar) Foo() {}
Большая часть кода не использует такие ограничения, поскольку они ограничивают полезность идеи интерфейса. Иногда, однако, они необходимы для устранения неоднозначностей среди аналогичных интерфейсов.
Это может помочь вам.
d := dog.(*Dog)
d.WagTail()
На детской площадке: https://play.golang.org/p/KlNqpmvFTJi
Вы можете определенно сделать это, один такой метод использует утверждение типа, как показано в другом ответе здесь. В противном случае ответ @Himanshu здесь очень хорошо описывает ситуацию.
Я хотел бы добавить к обсуждению, чтобы дополнительно описать, как вы
может иметь структуру придерживаться интерфейса, а также определять свои собственные методы
MakeDog
Метод возвращает Animal, и есть ряд причин, по которым вы можете рассмотреть возможность возврата Dog
(или конкретный тип) напрямую.
Причина, по которой я об этом говорю, заключается в том, что кто-то рассказал мне о создании методов, когда я впервые начал программировать на Go:
Принять интерфейс и вернуть конкретный тип (такой как структура)
Интерфейсы могут принимать любой конкретный тип. Вот почему они используются, когда вы не знаете тип аргумента, который вы передаете функции.
Я сделал поиск в Google со следующими терминами, и появилось немало статей
golang принимает интерфейс, возвращает структуру
Например: https://mycodesmells.com/post/accept-interfaces-return-struct-in-go и http://idiomaticgo.com/post/best-practice/accept-interfaces-return-structs/
Я собрал небольшую демонстрацию, расширяющую ваши концепции в вашем вопросе, чтобы попытаться четко описать методы интерфейса, а также методы и атрибуты для конкретных типов.
Взято из этого фрагмента на детской площадке
package main
import (
"fmt"
)
type Animal interface {
MakeNoise() string
}
// printNoise accepts any animal and prints it's noise
func printNoise(a Animal) {
fmt.Println(a.MakeNoise())
}
type pet struct {
nLegs int
color string
}
// describe is available to all types of Pet, but not for an animal
func (p pet) describe() string {
return fmt.Sprintf(`My colour is "%s", and I have "%d" legs`, p.color, p.nLegs)
}
type Dog struct {
pet
favouriteToy string
}
// MakeNoise implements the Animal interface for type Dog
func (Dog) MakeNoise() string {
return "Bark!"
}
// WagTail is something only a Dog can do
func (d Dog) WagTail() {
fmt.Println("I am a dog,", d.pet.describe(), ": Wags Tail")
}
type Cat struct {
pet
favouriteSleepingPlace string
}
// MakeNoise implements the Animal interface for type Cat
func (Cat) MakeNoise() string {
return "Meow!"
}
// ArchBack is something only a Cat can do
func (c Cat) ArchBack() {
fmt.Println("I am a cat,", c.pet.describe(), ": Arches Back")
}
type Bird struct {
pet
favoritePerch string
}
// MakeNoise implements the Animal interface for type Cat
func (Bird) MakeNoise() string {
return "Tweet!"
}
// Hop is something only a Bird can do
func (c Bird) Hop() {
fmt.Println("I am a bird,", c.pet.describe(), ": Hops to a different perch")
}
func main() {
dog := Dog{
pet: pet{nLegs: 4, color: "Brown"},
favouriteToy: "Ball",
}
printNoise(dog)
dog.WagTail()
cat := Cat{
pet: pet{nLegs: 4, color: "Tabby"},
favouriteSleepingPlace: "Sofa",
}
printNoise(cat)
cat.ArchBack()
bird := Bird{
pet: pet{nLegs: 2, color: "Rainbow"},
favoritePerch: "Back of Cage",
}
printNoise(bird)
bird.Hop()
}
Да, вы можете запускать методы, не являющиеся частью интерфейса. В коде было две проблемы, мешающие его правильной работе.
- Функция NewDog возвращает тип Animal. Метод WagTale прикреплен к Dog, а не Animal, который выдал ошибку. Тип возвращаемого значения изменен на Dog.
- Указатели в этом коде не нужны, и они были удалены.
После этих настроек код работает и все методы выполняются должным образом.
Ссылка на игровую площадку Go: https://play.golang.org/p/LYZJiQND7WW
package main
import (
"fmt"
)
type Animal interface {
MakeNoise()
}
type Dog struct {
color string
}
/* Interface implementation */
func (d Dog) MakeNoise() {
fmt.Println("Bark!")
}
/* End Interface implementation */
func (d Dog) WagTail() {
fmt.Println(d.color + " dog: Wag wag")
}
func NewDog(color string) Dog {
return Dog{color}
}
func main() {
dog := NewDog("Brown")
dog.MakeNoise()
dog.WagTail()
}