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.

Veja também