Interfaces
Esta aula aborda interfaces em Go, incluindo definição, implementação implícita, interface vazia (any) e polimorfismo. Você aprenderá como as interfaces permitem escrever código flexível e reutilizável, com exemplos práticos e exercícios.
Interfaces são um dos pilares da programação em Go, permitindo definir comportamentos que tipos concretos devem implementar. Diferente de outras linguagens, Go adota um sistema de interfaces implícitas, onde um tipo satisfaz uma interface automaticamente se implementar seus métodos, sem necessidade de declaração explícita. Isso promove flexibilidade e desacoplamento.
Nesta aula, exploraremos desde a definição básica de interfaces até o uso da interface vazia (any) e como o polimorfismo é alcançado em Go. Ao final, você será capaz de projetar código que depende de comportamentos, não de tipos concretos.
Definição
Uma interface em Go é um tipo abstrato que define um conjunto de métodos (assinaturas). Qualquer tipo que implemente todos esses métodos satisfaz a interface implicitamente. A sintaxe para declarar uma interface usa a palavra-chave type seguida do nome e da palavra interface.
Exemplo: definimos uma interface Forma que exige um método Area() float64. Qualquer tipo que tenha um método com essa assinatura automaticamente implementa a interface.
type Forma interface {
Area() float64
}Implementação implícita
Em Go, não há palavra-chave implements. Um tipo implementa uma interface simplesmente fornecendo métodos com as mesmas assinaturas. Isso é chamado de "duck typing" estrutural: se se comporta como um pato (tem os métodos), então é um pato.
Veja como um tipo concreto Retangulo implementa a interface Forma:
type Retangulo struct {
Largura, Altura float64
}
func (r Retangulo) Area() float64 {
return r.Largura * r.Altura
}Agora Retangulo satisfaz Forma e pode ser usado onde Forma é esperado. Isso permite que funções recebam qualquer tipo que implemente a interface, promovendo reutilização.
Interface vazia (any)
A interface vazia, denotada por interface{} ou any (a partir do Go 1.18), não possui métodos. Portanto, todos os tipos em Go a implementam. É útil quando você precisa armazenar valores de tipos arbitrários, como em contêineres genéricos (antes da introdução de genéricos).
Exemplo: uma função que aceita qualquer valor:
func ImprimirValor(v interface{}) {
fmt.Println(v)
}
// ou usando any
func ImprimirValor(v any) {
fmt.Println(v)
}Para recuperar o valor original, é necessário usar type assertion ou type switch.
var x any = 42
valor := x.(int) // type assertion, pânico se não for int
fmt.Println(valor)
switch v := x.(type) {
case int:
fmt.Println("Inteiro:", v)
case string:
fmt.Println("String:", v)
}Polimorfismo
Polimorfismo em Go é alcançado através de interfaces. Uma função que recebe uma interface pode operar em qualquer tipo que a implemente, permitindo comportamentos diferentes para tipos diferentes.
Exemplo: uma função CalcularAreaTotal que recebe um slice de Forma e soma as áreas:
func CalcularAreaTotal(formas []Forma) float64 {
total := 0.0
for _, f := range formas {
total += f.Area()
}
return total
}Podemos passar um slice contendo Retangulo e Circulo (que também implementa Forma):
type Circulo struct {
Raio float64
}
func (c Circulo) Area() float64 {
return math.Pi * c.Raio * c.Raio
}
func main() {
formas := []Forma{
Retangulo{Largura: 3, Altura: 4},
Circulo{Raio: 5},
}
fmt.Println("Área total:", CalcularAreaTotal(formas))
}Isso demonstra polimorfismo: o mesmo código funciona com diferentes tipos, desde que implementem a interface.
Boas práticas
- Defina interfaces pequenas e focadas (princípio da segregação de interfaces).
- Prefira aceitar interfaces e retornar tipos concretos.
- Use a interface vazia com moderação; prefira tipos concretos ou genéricos (a partir do Go 1.18).
- Nomeie interfaces com sufixo "-dor" ou "-or" (ex:
Writer,Reader).
Referências
- Effective Go: Interfaces
- Go Specification: Interface types
- A Tour of Go: Interfaces
- Go Blog: Type Parameters (Generics)
- Go Doc: any
Exercícios
Defina uma interface chamada
Stringercom um métodoString() string. Crie um tipoPessoa(com campos Nome e Idade) que implemente essa interface retornando uma string formatada.✓ Resposta:type Stringer interface { String() string } type Pessoa struct { Nome string Idade int } func (p Pessoa) String() string { return fmt.Sprintf("%s, %d anos", p.Nome, p.Idade) }Escreva uma função
Imprimirque aceite um valor do tipoanye o imprima usandofmt.Println. Em seguida, chame a função com um inteiro, uma string e um slice.✓ Resposta:func Imprimir(v any) { fmt.Println(v) } func main() { Imprimir(42) Imprimir("Olá") Imprimir([]int{1, 2, 3}) }Crie uma interface
Calculadoracom métodosSomar(a, b int) inteSubtrair(a, b int) int. Implemente a interface com um tipo concretoMinhaCalculadora.✓ Resposta:type Calculadora interface { Somar(a, b int) int Subtrair(a, b int) int } type MinhaCalculadora struct{} func (m MinhaCalculadora) Somar(a, b int) int { return a + b } func (m MinhaCalculadora) Subtrair(a, b int) int { return a - b }Utilizando a interface
Formado exemplo da aula, crie um tipoTriangulo(com base e altura) que implementeArea() float64. Em seguida, escreva uma função que receba um slice deFormae imprima a área de cada uma.✓ Resposta:type Triangulo struct { Base, Altura float64 } func (t Triangulo) Area() float64 { return (t.Base * t.Altura) / 2 } func ImprimirAreas(formas []Forma) { for _, f := range formas { fmt.Println(f.Area()) } }Explique por que o seguinte código não compila e corrija-o:
var x interface{} = 10; y := x + 5.✓ Resposta:O código não compila porquexé do tipointerface{}, e não é possível realizar operações aritméticas diretamente. É necessário fazer type assertion para um tipo numérico antes de somar. Correção:y := x.(int) + 5(assumindo que x seja int).