Na sua forma mais simples, um tipo de interface é um conjunto de assinaturas de métodos.
Aqui está um exemplo de uma definição de interface que inclui dois métodos, Add e Value:
type Counter interface {
Add(increment int)
Value() int
}
Os nomes dos parâmetros, como increment, podem ser omitidos da definição da interface, mas muitas vezes aumentam a legibilidade.
Os nomes de interfaces em Go não contêm a palavra Interface nem I.
Em vez disso, terminam frequentemente em er, por exemplo Reader ou Stringer.
Qualquer tipo que defina os métodos da interface implementa-a automaticamente, de forma implícita.
Não existe a palavra-chave implements em Go.
O tipo seguinte implementa a interface Counter que vimos acima.
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 a interface, não importa se o método tem um recetor de valor ou de ponteiro. (Volta aos conceitos de métodos se tiveres dúvidas sobre isto.)
Um valor de tipo de interface pode guardar qualquer valor que implemente esses métodos. 1
Isto significa que Stats pode agora ser usado em todos os locais que esperam a interface Counter.
func SetUpAnalytics(counter Counter) {
// ...
}
stats := Stats{}
SetUpAnalytics(stats)
// works because Stats implements Counter
Se todos os parâmetros de função e valores devolvidos tivessem de usar tipos concretos (ou seja, não-interface), isso limitaria fortemente a sua reutilização. Ao usar tipos de interface nos parâmetros, define-se apenas o comportamento de que a função precisa para fazer o seu trabalho. Assim, a função pode ser usada com parâmetros de tipos concretos diferentes, desde que esses tipos concretos implementem a interface. A mesma lógica aplica-se quando se usam tipos de interface como valores devolvidos.
Como as interfaces são implementadas implicitamente, um tipo pode implementar várias interfaces com facilidade. Basta que tenha todos os métodos necessários definidos.
As definições de interface podem incluir outras interfaces. Aqui está um exemplo da biblioteca padrão.
type ReadCloser interface {
Reader
Closer
}
// Based on these interfaces:
type Reader interface {
Read(p []byte) (n int, err error)
}
type Closer interface {
Close() error
}
Em vez de repetir as assinaturas dos métodos Read e Close para definir ReadCloser, a definição da interface recorre às definições existentes de Reader e Closer.
Há um tipo de interface muito especial em Go: o tipo interface vazia, que não contém nenhum método.
A interface vazia escreve-se assim: interface{}.
Em Go 1.18 ou superior, também se pode usar any.
Foi definido como um alias.
Como a interface vazia não tem métodos, todos os tipos a implementam implicitamente. Isto é útil para definir uma função que possa aceitar genericamente qualquer valor. Nesse caso, o parâmetro da função usa o tipo interface vazia.
Por exemplo:
func AnythingGoes(i interface{}) string {
if i == 0 {
return "zero"
}
if i == "" {
return "empty string"
}
return "something else"
}