Interface
Definition
An interface type is defined as a set of method signatures.
A value of interface type can hold any value that implements those methods.
Note: There is an error in the example code on line 22. Vertex
(the value type) doesn't implement Abser
because the Abs
method is defined only on *Vertex
(the pointer type).
Interface names
By convention, one-method interfaces are named by the method name plus an -er suffix or similar modification to construct an agent noun: Reader, Writer, Formatter, CloseNotifier etc.
Interfaces are implemented implicitly
A type implements an interface by implementing its methods. There is no explicit declaration of intent, no "implements" keyword.
Implicit interfaces decouple the definition of an interface from its implementation, which could then appear in any package without prearrangement.
Interface values
Under the hood, interface values can be thought of as a tuple of a value and a concrete type:
An interface value holds a value of a specific underlying concrete type.
Calling a method on an interface value executes the method of the same name on its underlying type.
Interface values with nil underlying values
If the concrete value inside the interface itself is nil, the method will be called with a nil receiver.
{% hint style="info" %} Note that an interface value that holds a nil concrete value is itself non-nil. {% endhint %}
Nil interface values
A nil interface value holds neither value nor concrete type.
Calling a method on a nil interface is a run-time error because there is no type inside the interface tuple to indicate which concrete method to call.
The empty interface
The interface type that specifies zero methods is known as the empty interface:
An empty interface may hold values of any type. (Every type implements at least zero methods.)
Empty interfaces are used by code that handles values of unknown type. For example, fmt.Print
takes any number of arguments of type interface{}
.
Type assertions
A type assertion provides access to an interface value's underlying concrete value.
This statement asserts that the interface value i
holds the concrete type T
and assigns the underlying T
value to the variable t
.
If i
does not hold a T
, the statement will trigger a panic.
To test whether an interface value holds a specific type, a type assertion can return two values: the underlying value and a boolean value that reports whether the assertion succeeded.
If i
holds a T
, then t
will be the underlying value and ok
will be true.
If not, ok
will be false and t
will be the zero value of type T
, and no panic occurs.
Note the similarity between this syntax and that of reading from a map.
Type switches
A type switch is a construct that permits several type assertions in series.
A type switch is like a regular switch statement, but the cases in a type switch specify types (not values), and those values are compared against the type of the value held by the given interface value.
The declaration in a type switch has the same syntax as a type assertion i.(T)
, but the specific type T
is replaced with the keyword type
.
This switch statement tests whether the interface value i
holds a value of type T
or S
. In each of the T
and S
cases, the variable v
will be of type T
or S
respectively and hold the value held by i
. In the default case (where there is no match), the variable v
is of the same interface type and value as i
.
Practices
- Use many, small interfaces
- Single method interfaces are some of the most powerful and flexible
- io.Writer, io.Reader, interface{}
- Single method interfaces are some of the most powerful and flexible
- Don't export interfaces for types that will be consumed
- Do export interfaces for types that will be used by package
- Design functions and methods to receive interfaces whenever possible
- concrete type for fields access
- but interfaces for behaviours
Stringers
One of the most ubiquitous interfaces is Stringer
defined by the fmt
package.
A Stringer
is a type that can describe itself as a string. The fmt
package (and many others) look for this interface to print values.
Errors
Go programs express error state with error
values.
The error
type is a built-in interface similar to fmt.Stringer
:
(As with fmt.Stringer
, the fmt
package looks for the error
interface when printing values.)
Functions often return an error
value, and calling code should handle errors by testing whether the error equals nil
.
A nil error
denotes success; a non-nil error
denotes failure.
Infinite loop
Because the implementation of fmt
package, error
type was checked before Stringer
.
Readers
The io
package specifies the io.Reader
interface, which represents the read end of a stream of data.
The Go standard library contains many implementations of these interfaces, including files, network connections, compressors, ciphers, and others.
The io.Reader
interface has a Read
method:
Read
populates the given byte slice with data and returns the number of bytes populated and an error value. It returns an io.EOF
error when the stream ends.
The example code creates a strings.Reader
and consumes its output 8 bytes at a time.
Images
Package image defines the Image
interface:
Note: the Rectangle
return value of the Bounds
method is actually an image.Rectangle
, as the declaration is inside package image
.
(See the documentation for all the details.)
The color.Color
and color.Model
types are also interfaces, but we'll ignore that by using the predefined implementations color.RGBA
and color.RGBAModel
. These interfaces and types are specified by the image/color package