O que é Interface em Go?
Uma interface em Go é um tipo que define um conjunto de assinaturas de métodos. Qualquer tipo que implemente todos esses métodos satisfaz a interface automaticamente — sem necessidade de declarar explicitamente que implementa aquela interface. Esse comportamento é chamado de implementação implícita (ou “duck typing estático”) e é uma das características mais poderosas e elegantes da linguagem Go.
Enquanto em linguagens como Java ou C# você precisa usar implements ou extends, em Go a relação é estrutural: se um tipo tem os métodos certos, ele implementa a interface. Ponto. Isso torna o código mais desacoplado, flexível e fácil de testar.
Declaração básica
type Animal interface {
Falar() string
Nome() string
}
Qualquer tipo com os métodos Falar() string e Nome() string satisfaz a interface Animal:
type Cachorro struct {
nome string
}
func (c Cachorro) Falar() string { return "Au au!" }
func (c Cachorro) Nome() string { return c.nome }
type Gato struct {
nome string
}
func (g Gato) Falar() string { return "Miau!" }
func (g Gato) Nome() string { return g.nome }
Agora ambos Cachorro e Gato implementam Animal, sem nenhuma palavra-chave especial:
func apresentar(a Animal) {
fmt.Printf("%s diz: %s\n", a.Nome(), a.Falar())
}
func main() {
apresentar(Cachorro{nome: "Rex"}) // Rex diz: Au au!
apresentar(Gato{nome: "Mimi"}) // Mimi diz: Miau!
}
Interfaces da biblioteca padrão
A biblioteca padrão do Go é construída sobre interfaces. As mais importantes que todo desenvolvedor Go deve conhecer são:
io.Reader e io.Writer
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
Essas interfaces de apenas um método conectam praticamente toda operação de I/O em Go — arquivos, conexões de rede, buffers, compressão, criptografia, HTTP. Ao implementar io.Reader ou io.Writer, seu tipo se integra automaticamente com todo o ecossistema.
fmt.Stringer
type Stringer interface {
String() string
}
Implemente String() para que fmt.Println exiba seu tipo de forma personalizada:
type Produto struct {
Nome string
Preco float64
}
func (p Produto) String() string {
return fmt.Sprintf("%s (R$ %.2f)", p.Nome, p.Preco)
}
error
type error interface {
Error() string
}
A interface error é a base de todo o sistema de tratamento de erros em Go. Qualquer tipo com o método Error() string pode ser usado como erro. Veja mais em tratamento de erros em Go.
Interface vazia (any)
A interface vazia interface{} (ou seu alias any desde o Go 1.18) é satisfeita por qualquer tipo, pois não exige nenhum método:
func imprimir(valor any) {
fmt.Println(valor)
}
imprimir(42) // int
imprimir("texto") // string
imprimir(true) // bool
imprimir([]int{1,2,3}) // slice
A interface vazia é usada quando um valor pode ser de qualquer tipo — como em fmt.Println, json.Marshal, e containers genéricos antes dos generics. Com a introdução de generics no Go 1.18, muitos usos de interface{} podem ser substituídos por parâmetros de tipo para maior segurança.
Type assertion e type switch
Type assertion
Para extrair o tipo concreto de um valor de interface, use type assertion:
var valor any = "Olá, Go!"
// Type assertion com verificação (recomendado)
texto, ok := valor.(string)
if ok {
fmt.Println("É string:", texto)
}
// Type assertion sem verificação (causa panic se falhar)
texto = valor.(string) // ok, mas arriscado
Type switch
Quando precisa tratar múltiplos tipos, use type switch:
func descrever(valor any) string {
switch v := valor.(type) {
case int:
return fmt.Sprintf("Inteiro: %d", v)
case string:
return fmt.Sprintf("Texto: %s", v)
case bool:
return fmt.Sprintf("Booleano: %t", v)
default:
return fmt.Sprintf("Tipo desconhecido: %T", v)
}
}
Composição de interfaces
Interfaces em Go podem ser compostas por outras interfaces, criando interfaces maiores a partir de menores:
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
type ReadWriter interface {
Reader
Writer
}
Esse padrão de composição é preferido em Go em vez de hierarquias profundas de herança. Mantenha interfaces pequenas — idealmente de um ou dois métodos — e componha quando necessário.
Boas práticas
Interfaces pequenas
A comunidade Go favorece interfaces com poucos métodos. As interfaces mais úteis da biblioteca padrão (io.Reader, io.Writer, fmt.Stringer, error) têm apenas um método. Interfaces pequenas são mais fáceis de implementar, testar e compor.
Defina interfaces no consumidor
Em Go, a convenção é definir a interface onde ela é usada (no pacote consumidor), não onde é implementada. Isso inverte a abordagem de Java/C# e mantém os pacotes desacoplados:
// pacote servico — define a interface que precisa
type RepositorioUsuario interface {
BuscarPorID(id int) (*Usuario, error)
}
type Servico struct {
repo RepositorioUsuario
}
// pacote postgres — implementa sem saber da interface
type PostgresRepo struct { db *sql.DB }
func (r *PostgresRepo) BuscarPorID(id int) (*Usuario, error) {
// implementação...
}
Use interfaces para testes
Interfaces tornam o código facilmente testável com mocks:
type EmailSender interface {
Enviar(destino, assunto, corpo string) error
}
// No teste
type MockEmailSender struct {
Chamadas int
}
func (m *MockEmailSender) Enviar(_, _, _ string) error {
m.Chamadas++
return nil
}
Para um guia completo, veja o artigo Interfaces em Go.
Perguntas Frequentes
Preciso declarar que meu tipo implementa uma interface?
Não. Em Go, a implementação de interfaces é implícita. Se o seu tipo possui todos os métodos que a interface exige, ele a implementa automaticamente. Não existe palavra-chave implements. Isso permite que tipos de pacotes diferentes satisfaçam interfaces que nem conhecem, tornando o código naturalmente desacoplado.
Qual a diferença entre interface e generics?
Interfaces definem comportamento (conjunto de métodos) e são resolvidas em tempo de execução via dispatch dinâmico. Generics definem restrições de tipo e são resolvidos em tempo de compilação, gerando código especializado para cada tipo. Use interfaces quando precisa de polimorfismo de comportamento; use generics quando precisa de código reutilizável com tipos diferentes mas sem overhead de runtime.
O que é a interface vazia (any)?
A interface vazia interface{} (alias any desde Go 1.18) não exige nenhum método, então é satisfeita por qualquer tipo. É usada quando você precisa aceitar valores de qualquer tipo, como em funções variádicas (fmt.Println) ou decodificação JSON genérica. Porém, use com moderação — preferir tipos concretos ou generics quando possível garante mais segurança em tempo de compilação.
Ponteiro ou valor em métodos de interface?
Se um método é definido com receiver de ponteiro (func (t *Tipo) Metodo()), apenas ponteiros para o tipo satisfazem a interface. Se o receiver é de valor (func (t Tipo) Metodo()), tanto valores quanto ponteiros satisfazem. A regra prática: use ponteiro quando o método precisa modificar o receptor ou quando o struct é grande; use valor para tipos pequenos e imutáveis.