A type satisfies an interface if it possesses all the methods
the interface requires.
For example, an *os.File
satisfies io.Reader
, Writer
,
Closer
, and ReadWriter
.
A *bytes.Buffer
satisfies Reader
, Writer
,
and ReadWriter
, but does not satisfy Closer
because it does not have a Close
method.
As a shorthand, Go programmers often say that a concrete type “is a”
particular interface type, meaning that it satisfies the interface.
For example, a *bytes.Buffer
is an io.Writer
; an
*os.File
is an io.ReadWriter
.
The assignability rule (§2.4.2) for interfaces is very simple: an expression may be assigned to an interface only if its type satisfies the interface. So:
var w io.Writer w = os.Stdout // OK: *os.File has Write method w = new(bytes.Buffer) // OK: *bytes.Buffer has Write method w = time.Second // compile error: time.Duration lacks Write method var rwc io.ReadWriteCloser rwc = os.Stdout // OK: *os.File has Read, Write, Close methods rwc = new(bytes.Buffer) // compile error: *bytes.Buffer lacks Close method
This rule applies even when the right-hand side is itself an interface:
w = rwc // OK: io.ReadWriteCloser has Write method rwc = w // compile error: io.Writer lacks Close method
Because ReadWriter
and ReadWriteCloser
include all
the methods of Writer
, any type that satisfies
ReadWriter
or ReadWriteCloser
necessarily
satisfies Writer
.
Before we go further, we should explain one subtlety in what it means
for a type to have a method.
Recall from Section 6.2 that for each
named concrete type T
, some of its methods have a receiver of
type T
itself whereas others require a *T
pointer.
Recall also that it is legal to call a *T
method on an argument
of type T
so long as the argument is a variable; the
compiler implicitly takes its address.
But this is mere syntactic sugar:
a value of type T
does not possess all the
methods that a *T
pointer does,
and as a result it might satisfy fewer interfaces.
An example will make this clear.
The String
method of the IntSet
type
from Section 6.5 requires a pointer receiver,
so we cannot call that method on a non-addressable IntSet
value:
type IntSet struct { /* ... */ } func (*IntSet) String() string var _ = IntSet{}.String() // compile error: String requires *IntSet receiver
but we can call it on an IntSet
variable:
var s IntSet var _ = s.String() // OK: s is a variable and &s has a String method
However, since only *IntSet
has a String
method, only
*IntSet
satisfies the fmt.Stringer
interface:
var _ fmt.Stringer = &s // OK var _ fmt.Stringer = s // compile error: IntSet lacks String method
Section 12.8 includes a program that
prints the methods of an arbitrary value,
and the godoc -analysis=type
tool
(§10.7.4) displays the methods of each type and
the relationship between interfaces and concrete types.
Like an envelope that wraps and conceals the letter it holds, an interface wraps and conceals the concrete type and value that it holds. Only the methods revealed by the interface type may be called, even if the concrete type has others:
os.Stdout.Write([]byte("hello")) // OK: *os.File has Write method os.Stdout.Close() // OK: *os.File has Close method var w io.Writer w = os.Stdout w.Write([]byte("hello")) // OK: io.Writer has Write method w.Close() // compile error: io.Writer lacks Close method
An interface with more methods, such as io.ReadWriter
, tells us
more about the values it contains, and places greater demands on
the types that implement it, than does an interface with fewer
methods such as io.Reader
.
So what does the type interface{}
, which has no methods at all, tell
us about the concrete types that satisfy it?
That’s right: nothing.
This may seem useless, but in fact the type interface{}
, which is called the
empty interface type, is indispensable.
Because the empty interface type places no demands on the types
that satisfy it, we can assign any value to the empty
interface.
var any interface{} any = true any = 12.34 any = "hello" any = map[string]int{"one": 1} any = new(bytes.Buffer)
Although it wasn’t obvious, we’ve been using the empty interface type
since the very first example in this book, because it is what allows
functions like fmt.Println
, or errorf
in Section 5.7, to accept arguments of any type.
Of course, having created an interface{}
value containing a
boolean, float, string, map, pointer, or any other type, we can do nothing
directly to the value it holds since the interface has no methods.
We need a way to get the value back out again.
We’ll see how to do that using a type assertion in Section 7.10.
Since interface satisfaction depends only on the methods of the
two types involved, there is no need to declare the relationship
between a concrete type and the interfaces it satisfies.
That said, it is occasionally useful to document and assert the
relationship when it is intended but not otherwise enforced by the
program.
The declaration below asserts at compile time that a value of type
*bytes.Buffer
satisfies io.Writer
:
// *bytes.Buffer must satisfy io.Writer var w io.Writer = new(bytes.Buffer)
We needn’t allocate a new variable since any value of type
*bytes.Buffer
will do, even nil
, which we write as
(*bytes.Buffer)(nil)
using an explicit conversion.
And since we never intend to refer to w
, we can replace it with
the blank identifier.
Together, these changes give us this more frugal variant:
// *bytes.Buffer must satisfy io.Writer var _ io.Writer = (*bytes.Buffer)(nil)
Non-empty interface types such as io.Writer
are most often
satisfied by a pointer type, particularly when one or more of the
interface methods implies some kind of mutation to the receiver,
as the Write
method does.
A pointer to a struct is an especially common method-bearing type.
But pointer types are by no means the only types that satisfy
interfaces, and even interfaces with mutator methods may be satisfied
by one of Go’s other reference types.
We’ve seen examples of slice types with methods
(geometry.Path
, §6.1)
and map types with methods (url.Values
, §6.2.1), and later we’ll see a function type with methods
(http.HandlerFunc
, §7.7).
Even basic types may satisfy interfaces; as we saw in Section 7.4, time.Duration
satisfies
fmt.Stringer
.
A concrete type may satisfy many unrelated interfaces. Consider a program that organizes or sells digitized cultural artifacts like music, films, and books. It might define the following set of concrete types:
Album Book Movie Magazine Podcast TVEpisode Track
We can express each abstraction of interest as an interface. Some properties are common to all artifacts, such as a title, a creation date, and a list of creators (authors or artists).
type Artifact interface { Title() string Creators() []string Created() time.Time }
Other properties are restricted to certain types of artifacts. Properties of the printed word are relevant only to books and magazines, whereas only movies and TV episodes have a screen resolution.
type Text interface { Pages() int Words() int PageSize() int } type Audio interface { Stream() (io.ReadCloser, error) RunningTime() time.Duration Format() string // e.g., "MP3", "WAV" } type Video interface { Stream() (io.ReadCloser, error) RunningTime() time.Duration Format() string // e.g., "MP4", "WMV" Resolution() (x, y int) }
These interfaces are but one useful way to group related concrete
types together and express the facets they share in common.
We may discover other groupings later.
For example, if we find we need to handle Audio
and Video
items
in the same way, we can define a Streamer
interface to represent their
common aspects without changing any existing type declarations.
type Streamer interface { Stream() (io.ReadCloser, error) RunningTime() time.Duration Format() string }
Each grouping of concrete types based on their shared behaviors can be expressed as an interface type. Unlike class-based languages, in which the set of interfaces satisfied by a class is explicit, in Go we can define new abstractions or groupings of interest when we need them, without modifying the declaration of the concrete type. This is particularly useful when the concrete type comes from a package written by a different author. Of course, there do need to be underlying commonalities in the concrete types.