Go: Interfaces explained
An interface type is a named set of method signatures.
Basics
You can declare an interface like this:
type MyStringer interface {
String() string
}
A type implements an interface by implementing its methods.
In this example both Temp
and *Point
implement the MyStringer
interface:
type Temp int
func (t Temp) String() string {
return strconv.Itoa(int(t)) + " °C"
}
type Point struct {
x, y int
}
func (p *Point) String() string {
return fmt.Sprintf("(%d,%d)", p.x, p.y)
}
Actually, *Temp
also implements MyStringer
, since the method set of a pointer type *T
is the set of all methods with receiver *T
or T
.
A variable of interface type can hold any value that implements the interface methods. Calling a method on this value executes the method of its underlying type.
var x MyStringer
x = Temp(24)
fmt.Println(x.String()) // "24 °C"
x = &Point{1, 2}
fmt.Println(x.String()) // "(1,2)"
Structural typing
Any type that provides the methods named in an interface may be treated as an implementation of that interface. No explicit declaration is required.
In fact, the Temp
, *Temp
and *Point
types also implement the fmt.Stringer
interface. The String
method in this interface is used to print values passed as an operand to functions such as fmt.Println
:
var x MyStringer
x = Temp(24)
fmt.Println(x) // "24 °C"
x = &Point{1, 2}
fmt.Println(x) // "(1,2)"
The empty interface
The interface type that specifies zero methods is known as the empty interface:
interface{}
A variable of empty interface type can hold values of any type since every type implements at least zero methods:
var x interface{}
x = 2.4
fmt.Println(x) // "2.4"
x = &Point{1, 2}
fmt.Println(x) // "(1,2)"
The fmt.Println
function is a chief example from the standard library. It takes any number of arguments of any type:
func Println(a ...interface{}) (n int, err error)
Interface values
A interface value is represented as a pair of a concrete value and a dynamic type:
[Value, Type]
In a call to fmt.Printf
, you can use %v
to print the concrete value and %T
to print the dynamic type:
var x MyStringer
fmt.Printf("%v %T\n", x, x) // "<nil> <nil>"
x = Temp(24)
fmt.Printf("%v %T\n", x, x) // "24 °C main.Temp"
x = &Point{1, 2}
fmt.Printf("%v %T\n", x, x) // "(1,2) *main.Point"
x = (*Point)(nil)
fmt.Printf("%v %T\n", x, x) // "<nil> *main.Point"
The zero value of an interface type is nil, which is represented as [nil, nil]
.
Calling a method on a nil interface is a run-time error. However, it's quite common to write methods that can handle a receiver value [nil, T]
, where T != nil
.
You can also use type assertions, type switches and reflection to access the dynamic type of an interface value. The article Find the type of an object has more details.
Equality
Two interface values are equal if they have equal concrete values and identical dynamic types, or if both are nil.
A value t
of interface type T
and a value x
of non-interface type X
are equal if
t
's concrete value is equal tox
andt
's dynamic type is identical toX
.
var x MyStringer
fmt.Println(x, x == nil) // "<nil> true"
x = (*Point)(nil)
fmt.Println(x, x == nil) // "<nil> false"
In the second print statement, the concrete value of x
equals nil
, but its dynamic type is *Point
, which is not nil
.