Una interfaz en Go define un conjunto de firmas de métodos — un contrato de comportamiento. Lo crucial es que las interfaces se satisfacen implícitamente: cualquier tipo que tenga esos métodos satisface automáticamente la interfaz, sin necesidad de una declaración explícita "implements". Este enfoque estructural, de duck-typing, es distintivo y poderoso.
Definir e implementar implícitamente una interfaz
// define an interface — a set of method signatures
type Speaker interface {
Speak() string
}
type Dog struct{}
func (d Dog) Speak() string { return "Woof" } // Dog has Speak() → it IS a Speaker
type Cat struct{}
func (c Cat) Speak() string { return "Meow" } // Cat too — implicitly
// NO "implements Speaker" anywhere — satisfaction is automatic
var s Speaker = Dog{} // works because Dog has the Speak() method
s.Speak() // "Woof"
No existe la palabra clave implements — si un tipo tiene todos los métodos de la interfaz, satisface la interfaz automáticamente. Esto desacopla la interfaz de sus implementaciones (la implementación ni siquiera necesita saber que la interfaz existe).
Polimorfismo a través de interfaces
func announce(s Speaker) { // accepts ANY type that has Speak()
fmt.Println(s.Speak())
}
announce(Dog{}) // "Woof"
announce(Cat{}) // "Meow"
speakers := []Speaker{Dog{}, Cat{}} // a slice of different types via the interface
for _, sp := range speakers { sp.Speak() }
Las interfaces permiten polimorfismo — escribe código contra el comportamiento, y cualquier tipo conforme funciona.
La interfaz vacía y any
var x interface{} // the empty interface — satisfied by EVERY type (no methods required)
x = 42
x = "hello"
// Go 1.18+: `any` is an alias for interface{}
func print(v any) { fmt.Println(v) } // accepts anything
La interfaz vacía (interface{} / any) contiene cualquier valor — se usa cuando el tipo no se conoce de antemano (aunque los genéricos ahora a menudo reemplazan esto).
Interfaces de la librería estándar que usarás constantemente
type Stringer interface { String() string } // controls fmt printing
type error interface { Error() string } // the error type itself!
type io.Reader interface { Read(p []byte) (n int, err error) }
type io.Writer interface { Write(p []byte) (n int, err error) }
Mucha de la librería estándar está construida sobre interfaces pequeñas (io.Reader/io.Writer, error, Stringer) — implementarlas hace que tus tipos funcionen con todo el ecosistema.
El idioma: acepta interfaces, devuelve structs
Go favors SMALL interfaces (often one method) defined where they're USED (the consumer),
not where types are defined. "The bigger the interface, the weaker the abstraction."
Por qué es importante
Las interfaces son centrales en el diseño de Go y su enfoque de abstracción y polimorfismo — y su satisfacción implícita (sin palabra clave implements; tipado estructural) es una de las características más distintivas y poderosas del lenguaje.
Este desacoplamiento significa que los tipos e interfaces evolucionan independientemente, puedes definir interfaces después de los tipos que las satisfacen, y cualquier tipo conforme se conecta automáticamente al código basado en interfaces — permitiendo diseños flexibles, débilmente acoplados y pruebas fáciles (reemplaza con implementaciones mock).
Comprender la implementación implícita, el polimorfismo a través de interfaces, la interfaz vacía/any, el idioma de interfaz pequeña ("acepta interfaces, devuelve structs"), y las ubicuas interfaces de la librería estándar (error, io.Reader/Writer, Stringer) es esencial para Go idiomático.
Es un tema frecuente en entrevistas precisamente porque el modelo implícito y estructural difiere tan fundamentalmente de las interfaces explícitas de Java/C#.
