I don't understand at which point an Interface method is being called. I'm looking at the following example from the Go Tour:
package main
import "fmt"
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}
func main() {
a := Person{"Arthur Dent", 42}
z := Person{"Zaphod Beeblebrox", 9001}
fmt.Println(a, z)
}
Problem:
I understand that the func (p Person)
receives the String()
method and that it returns the string
I want to display. But the fmt.Println
in the main()
method has to call String()
at some point, right?
I had a look at the source of fmt
in godoc, but I still cannot figure it out!
Another example:
If I add my own interface, lets say Stringer2
with a method called String2()
and then create a func (p Person) String2() {....}
. How does String()
get executed by fmt.Println
, but String2()
not?
The value is passed to Println
as an interface{}
, and is checked if it satisfies the fmt.Stringer
interface via a "type assertion" often in the form of a "type switch".
func IsStringer(i interface{}) {
switch s := i.(type) {
case fmt.Stringer:
fmt.Println("Person a has a String() method")
fmt.Println(s.String())
default:
fmt.Println("not a stringer")
}
// OR for a single type
if s, ok := i.(fmt.Stringer); ok {
fmt.Println("Person a has a String() method")
fmt.Println(s.String())
}
}
However, other methods may take precedence when printing from the fmt
package. There are first checks for fmt.Formatter
, fmt.GoStringer
, error
, and then finally fmt.Stringer
.
The fmt
package works with the interfaces it defines, in this case Stringer
. It does not know of interfaces defined by you so it wouldn't know to call String2()
even if you pass it a type that meets the Stringer2
interface.
Interfaces are a way to have common behavior between types. So if you create a function Foo(s Stringer2)
, Foo can simply call s.String2()
confident that anything passed into it will have the function String2()
.
To go a bit deeper, fmt.Println
takes interface{}
types and then uses reflection to check if the given argument meets the Stringer
interface to then call String()
.
Make sense?