En su forma más simple, un tipo de interfaz es un conjunto de firmas de métodos.
Aquí tienes un ejemplo de definición de interfaz que incluye dos métodos Add y Value:
type Counter interface {
Add(increment int)
Value() int
}
Los nombres de los parámetros como increment se pueden omitir en la definición de la interfaz, pero a menudo aumentan la legibilidad.
Los nombres de las interfaces en Go no contienen la palabra Interface ni I.
En su lugar, a menudo terminan en er, por ejemplo, Reader, Stringer.
Cualquier tipo que defina los métodos de la interfaz implementa la interfaz automática e implícitamente.
No existe la palabra clave implements en Go.
El siguiente tipo implementa la interfaz Counter que vimos antes.
type Stats struct {
value int
// ...
}
func (s Stats) Add(v int) {
s.value += v
}
func (s Stats) Value() int {
return s.value
}
func (s Stats) SomeOtherMethod() {
// The type can have additional methods not mentioned in the interface.
}
Para implementar la interfaz, no importa si el método tiene un receptor de valor o de puntero. (Vuelve a consultar los conceptos de métodos si no estás seguro sobre ellos.)
Un valor de tipo interfaz puede contener cualquier valor que implemente esos métodos. 1
Eso significa que Stats ahora se puede usar en todos los lugares que esperan la interfaz Counter.
func SetUpAnalytics(counter Counter) {
// ...
}
stats := Stats{}
SetUpAnalytics(stats)
// works because Stats implements Counter
Si todos los parámetros de función y valores devueltos necesitaran usar tipos concretos (es decir, que no sean interfaces), eso limitaría mucho su reutilización. Al usar tipos de interfaz para los parámetros, solo se define el comportamiento que la función necesita para hacer su trabajo. Ahora la función se puede usar con parámetros de diferentes tipos concretos, siempre que esos tipos concretos implementen la interfaz. La misma lógica se aplica al usar tipos de interfaz como valores devueltos.
Como las interfaces se implementan de forma implícita, un tipo puede implementar varias interfaces fácilmente. Solo necesita tener definidos todos los métodos necesarios.
Las definiciones de interfaces pueden incluir otras interfaces. Aquí tienes un ejemplo de la biblioteca estándar.
type ReadCloser interface {
Reader
Closer
}
// Based on these interfaces:
type Reader interface {
Read(p []byte) (n int, err error)
}
type Closer interface {
Close() error
}
En lugar de repetir las firmas de los métodos Read y Close para definir ReadCloser, la definición de la interfaz hace uso de las definiciones existentes de Reader y Closer.
Hay un tipo de interfaz muy especial en Go, el tipo interfaz vacía, que no contiene ningún método.
La interfaz vacía se escribe así: interface{}.
En Go 1.18 o superior, también se puede usar any.
Se definió como un alias.
Como la interfaz vacía no tiene métodos, todos los tipos la implementan de forma implícita. Esto es útil para definir una función que pueda aceptar cualquier valor de forma genérica. En ese caso, el parámetro de la función usa el tipo de interfaz vacía.
Por ejemplo:
func AnythingGoes(i interface{}) string {
if i == 0 {
return "zero"
}
if i == "" {
return "empty string"
}
return "something else"
}