Interfaces
suggest changeAn interface defines a set of methods on a type.
Interfaces are used to abstract behavior.
For example: a standard library defines io.Reader
interface:
type Reader interface {
Read(d []byte) (int, error)
}
Most functions that operate on streams of binary data (e.g. json decoder) take io.Reader
as a source of data. That way we can implement Reader
interface for physical files, bytes in memory, network connections and have json.Decode
work on all those sources.
Here’s how to define and implement a simple interface:
// Stringer is an interface with a single method
type Stringer interface {
String() string
}
// User struct that implements Stringer interface
type User struct {
Name string
}
func (u *User) String() string {
return u.Name
}
// Any type can implement an interface. Here we create
// an alias of int type an implement Stringer interface
type MyInt int
func (mi MyInt) String() string {
return strconv.Itoa(int(mi))
}
// printTypeAndString accepts an interface. 's' can be any value
// that implements Stringer interface
func printTypeAndString(s Stringer) {
fmt.Printf("%T: %s\n", s, s)
}
func main() {
u := &User{Name: "John"}
printTypeAndString(u)
n := MyInt(5)
printTypeAndString(n)
}
*main.User: John
main.MyInt: 5
Unlike most other languages, interfaces are satisfied implicitly.
We don’t have to explicitly declare that struct User
implements interface Stringer
.
Interfaces can only contain methods, not data. You can use struct embedding if you want to re-use both methods and data.
You can only define methods on types defined in the same package. We had to define type alias MyInt
because we can’t add methods to built-int type int
.