Reputation: 3000
I have an interface which defines a method. I have a struct which implements this interface. In it, I have implemented the methods from this interface and also have additional methods defined.
For example:
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()
}
On Playground: https://play.golang.org/p/B1GgoNToNl_l
Here, WagTail() is not part of the Animal interface but belongs to the Dog struct. Running this code gives an error
dog.WagTail undefined (type Animal has no field or method WagTail).
Is there a way I could have a struct adhere to an interface and also define it's own methods?
Upvotes: 4
Views: 4062
Reputation: 103
Yes, you can run methods that are not part of the interface. There were two issues in the code preventing it from running properly.
After these adjustments, the code runs and properly and all methods execute as expected.
Link on Go playground: 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()
}
Upvotes: 1
Reputation: 12675
The error described it all:
dog.WagTail undefined (type Animal has no field or method WagTail)
To implement an interface you should implement all methods defined inside it.
dog := NewDog("Brown")
dog.MakeNoise()
dog.WagTail()
Now NewDog
returns Animal interface which contains MakeNoise
method but not WagTail
.
The only way to manage your requirement is either create a variable of struct type Dog
and then you can call any method having Dog as receiver.
d := &Dog{"Brown"}
d.WagTail()
Or you can return the pointer to Dog
struct from NewDog
method just like you did in your code mentioned in the comment as:
func NewDog(color string) *Dog {
return &Dog{color}
}
But if the method is not defined in interface you cannot implement it using the struct as method receiver.
Golang provides a way in which:
You can ask the compiler to check that the type T implements the interface I by attempting an assignment using the zero value for T or pointer to T, as appropriate
type T struct{}
var _ I = T{} // Verify that T implements I.
var _ I = (*T)(nil) // Verify that *T implements I.
If T (or *T, accordingly) doesn't implement I, the mistake will be caught at compile time.
If you wish the users of an interface to explicitly declare that they implement it, you can add a method with a descriptive name to the interface's method set. For example:
type Fooer interface {
Foo()
ImplementsFooer()
}
A type must then implement the ImplementsFooer method to be a Fooer, clearly documenting the fact and announcing it in godoc's output.
type Bar struct{}
func (b Bar) ImplementsFooer() {}
func (b Bar) Foo() {}
Most code doesn't make use of such constraints, since they limit the utility of the interface idea. Sometimes, though, they're necessary to resolve ambiguities among similar interfaces.
Upvotes: 2
Reputation: 86
This may help you.
d := dog.(*Dog)
d.WagTail()
On Playground: https://play.golang.org/p/KlNqpmvFTJi
Upvotes: 6
Reputation: 2044
You can definitely do it, one such method is using type assertion as shown in the other answer here. Otherwise the answer by @Himanshu here describes the situation very well.
I would like to add to the discussion to further describe how you
could have a struct adhere to an interface and also define it's own methods
The MakeDog
method returns an Animal, and there are a number of reasons you may consider returning a Dog
(or whichever concrete type) directly.
The reason I bring this up is because of something someone told me about creating methods when I first started programming in Go:
Accept an Interface and return a Concrete type (such as a Struct)
Interfaces can accept any concrete type. That's why they are used when you don't know the type of argument you are passing to the function.
I did a google search with the following terms and quite a few articles turned up
golang accept interface, return struct
For example: https://mycodesmells.com/post/accept-interfaces-return-struct-in-go and http://idiomaticgo.com/post/best-practice/accept-interfaces-return-structs/
I have put together a little bit of a demo extending on your concepts in your question to try and clearly describe interface methods as well as methods and attributes for specific types
Taken from this snippet on Playground
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()
}
Upvotes: 2