---
id: ebf46089667a6469b258b8e52e57571f
author: Yunjin Lee
title: 'Interfaces e incorporação de structs em Go: diferenças em relação à herança de classes e design por composição'
description: Compara as diferenças entre a herança de classes em Java/C++ e a tipagem estrutural (structural typing) das interfaces em Go, e explica o padrão de composição (Composition) por meio de exemplos de código que utilizam a incorporação de structs (struct embedding) e a delegação de métodos.
language: pt
date: 2025-07-17T10:40:02.284435348Z
path: /blog/posts/go-interfaces-are-not-inheritance-zd04c1084
category: language
---

# Visão geral

As interfaces em Go permitem que várias structs possuam, com facilidade, funções com os mesmos argumentos e valores de retorno, porém isso difere da forma como o palavra-chave extends do Java estende e sobrescreve adequadamente o comportamento dessas funções internas. Somente ao compreender adequadamente a reutilização de código composicional do Go é que se pode evitar a confusão com herança, mas é difícil alcançar uma compreensão teoricamente perfeita desde o início. Vamos explorá-la com cenários propensos a erros.

## Erros comuns

Iniciantes podem cometer os seguintes erros.

```go
package main
import (
	"fmt"
	"strings"
)

type Fruits interface {
	GetBrix() float64
	GetName() string
	SetLabel()
	GetLabel(string) string
	PrintAll()
}

type Apple struct {
	Label string
	Name  string
	Brix  float64
}

type Watermelon struct {
	Label string
	Name  string
	Brix  float64
}

func (a *Apple) PrintAll() {
	fmt.Printf("Fruit: %s, Label: %s, Brix: %v\n", a.Name, a.Label, a.Brix)
}

const (
	NO_LABEL = "EMPTY LABEL"
)

func (a *Apple) SetLabel(lbl string) {
	a.Brix 	= 14.5;
	a.Name 	= "apple";
	lbl_lower := strings.ToLower(lbl)
	if strings.Contains(lbl_lower, a.Name) {
		fmt.Println("Succeed: Label was ", lbl)
		a.Label = lbl;
	} else {
		fmt.Println("Failed: Label was ", lbl)
		a.Label = NO_LABEL;
	}
}

func (w *Watermelon) SetLabel(lbl string) {
	w.Brix = 10;
	w.Name = "watermelon";
	lbl_lower := strings.ToLower(lbl)
	if strings.Contains(lbl_lower, w.Name) {
		w.Label = lbl;
	} else {
		w.Label = NO_LABEL;
	}
}

func main() {
	fmt.Println("Inheritance test #1")
	apple := new(Apple)
	watermelon := apple
	apple.SetLabel("Apple_1")
	fmt.Println("Apple, before copied to Watermelon")
	apple.PrintAll()
	watermelon.SetLabel("WaterMelon_2")
	fmt.Println("Apple, after copied to Watermelon")
	apple.PrintAll()
	fmt.Println("Watermelon, which inherited Apple's Method")
	watermelon.PrintAll()
}
```

Quem pensa que Go segue a herança tradicional poderá achar que esse código não apresenta problemas. No entanto, sua saída é a seguinte.

```
Inheritance test #1
Succeed: Label was  Apple_1
Apple, before copied to Watermelon
Fruit: apple, Label: Apple_1, Brix: 14.5
Failed: Label was  WaterMelon_2
Apple, after copied to Watermelon
Fruit: apple, Label: EMPTY LABEL, Brix: 14.5
Watermelon, which inherited Apple's Method
Fruit: apple, Label: EMPTY LABEL, Brix: 14.5
```

Aqui, o comportamento do Go torna-se apenas mais claro.

```go
watermelon := apple
```

Esse código não converte de forma alguma o Apple na classe Watermelon.
Na verdade, watermelon é apenas um ponteiro para apple.

Reiterando, **Go não segue o conceito tradicional de herança.**

Escrever código com esse mal-entendido causa erros graves, como a criação de ponteiros sem sentido e a cópia inesperada de funções para outras structs.

Então, como seria um código exemplar?

## Exemplo adequado em Go

```go
package main
import (
	"fmt"
	"strings"
)

type Fruits interface {
	GetBrix() float64
	GetName() string
	SetLabel()
	GetLabel(string) string
	PrintAll()
}

type BaseFruit struct {
	Name  string
	Brix  float64
}

type Apple struct {
	Label string
	Fruit BaseFruit
}

type Watermelon struct {
	Label string
	Fruit BaseFruit

}

func (b *BaseFruit) PrintAll() {
	fmt.Printf("Fruit: %s, Brix: %v\n", b.Name, b.Brix)
}


const (
	NO_LABEL = "EMPTY LABEL"
)

func (a *Apple) SetLabel(lbl string) {
	a.Fruit.Brix 	= 14.5;
	a.Fruit.Name 	= "apple";
	lbl_lower := strings.ToLower(lbl)
	if strings.Contains(lbl_lower, a.Fruit.Name) {
		fmt.Println("Succeed: Label was ", lbl)
		a.Label = lbl;
	} else {
		fmt.Println("Failed: Label was ", lbl)
		a.Label = NO_LABEL;
	}
	fmt.Printf("Fruit %s label set to %s\n", a.Fruit.Name, a.Label);
	a.Fruit.PrintAll()
}

func (w *Watermelon) SetLabel(lbl string) {
	w.Fruit.Brix = 10;
	w.Fruit.Name = "Watermelon";
	lbl_lower := strings.ToLower(lbl)
	if strings.Contains(lbl_lower, w.Fruit.Name) {
		w.Label = lbl;
	} else {
		w.Label = NO_LABEL;
	}
	fmt.Printf("Fruit %s label set to %s\n", w.Fruit.Name, w.Label);
	w.Fruit.PrintAll()
}

func main() {
	apple := new(Apple)
	watermelon := new(Watermelon)
	apple.SetLabel("Apple_1")
	watermelon.SetLabel("WaterMelon_2")
}
```

Porém, em Go também é possível fazer com que algo **pareça** herança.
Trata-se do exemplo de embedding anônimo. Isso é possível declarando a struct interna sem nome.
Nesse caso, é possível acessar **os campos da struct subordinada mesmo sem mencioná-la explicitamente**.
Usando esse padrão de **promoção** dos campos da struct subordinada para a struct superior, a legibilidade pode melhorar em alguns casos. Entretanto, recomenda-se não utilizá-lo quando for necessário mostrar explicitamente a struct subordinada.

```go
package main
import (
	"fmt"
	"strings"
)

type Fruits interface {
	GetBrix() float64
	GetName() string
	SetLabel()
	GetLabel(string) string
	PrintAll()
}

type BaseFruit struct {
	Name  string
	Brix  float64
}

type Apple struct {
	Label string
	BaseFruit
}

type Watermelon struct {
	Label string
	BaseFruit

}

func (b *BaseFruit) PrintAll() {
	fmt.Printf("Fruit: %s, Brix: %v\n", b.Name, b.Brix)
}


const (
	NO_LABEL = "EMPTY LABEL"
)

func (a *Apple) SetLabel(lbl string) {
	a.Brix 	= 14.5;
	a.Name 	= "apple";
	lbl_lower := strings.ToLower(lbl)
	if strings.Contains(lbl_lower, a.Name) {
		fmt.Println("Succeed: Label was ", lbl)
		a.Label = lbl;
	} else {
		fmt.Println("Failed: Label was ", lbl)
		a.Label = NO_LABEL;
	}
	fmt.Printf("Fruit %s label set to %s\n", a.Name, a.Label);
	a.PrintAll()
}

func (w *Watermelon) SetLabel(lbl string) {
	w.Brix = 10;
	w.Name = "Watermelon";
	lbl_lower := strings.ToLower(lbl)
	if strings.Contains(lbl_lower, w.Name) {
		w.Label = lbl;
	} else {
		w.Label = NO_LABEL;
	}
	fmt.Printf("Fruit %s label set to %s\n", w.Name, w.Label);
	w.PrintAll()
}

func main() {
	apple := new(Apple)
	watermelon := new(Watermelon)
	apple.SetLabel("Apple_1")
	watermelon.SetLabel("WaterMelon_2")
}
```

Neste exemplo apresenta a seguinte diferença.

```go
w.PrintAll() // Chamada automática por promoção através da struct anônima, em vez de w.Friut.PrintAll()
```

Os pontos importantes de ambos os exemplos são estes:

- main simples, funções separadas por funcionalidade
- structs diferentes, objetos diferentes
- quando for necessário compartilhar, utilizar a struct interna

Quais são as vantagens dessa filosofia de programação?

## Vantagens

- Distinção clara entre métodos que precisam ser compartilhados e os que não precisam
- Separação de responsabilidades entre structs e métodos individuais
- Código estruturalmente separado de acordo com a especificação das funcionalidades necessárias

No início, a linguagem Go pode parecer pouco familiar por ser diferente do OOP tradicional, mas, com a prática, torna-se possível programar de forma explícita.

## Resumo

- Isolar as responsabilidades
- Dividir detalhadamente por unidade de struct
- Métodos não devem ser entendidos como classes abstratas do Java
- Programar de forma explícita e concreta
  A linguagem Go deve ser tratada de forma mais simples e clara, individualmente, do que o modelo OOP tradicional. Em vez de programar de forma extensível, escrevamos separando em etapas e de forma estruturada.
