---
id: ebf46089667a6469b258b8e52e57571f
author: Yunjin Lee
title: 'Go-gränssnitt och strukturinbäddning: skillnader jämfört med klassarv och kompositionsdesign'
description: Denna text jämför skillnaderna mellan klassarv i Java/C++ och den strukturella typningen med gränssnitt i Go, och förklarar med kodexempel kompositionsmönstret (Composition) med hjälp av strukturinbäddning (struct embedding) och metoddelegering.
language: sv
date: 2025-07-17T10:40:02.284435348Z
path: /blog/posts/go-interfaces-are-not-inheritance-zd04c1084
category: language
---

# Översikt

Go-gränssnitt (interfaces) gör det enkelt för flera structar att ha funktioner med samma argument och returvärden, men det fungerar annorlunda än det sätt på vilket Javas extends-nyckelord utökar och överskuggar (overrides) beteendet hos de inneboende funktionerna. Man måste förstå Gos kompositionsbaserade kodåteranvändning ordentligt för att inte förväxla den med arv, men det är svårt att uppnå en teoretiskt fullständig förståelse från början. Låt oss undersöka detta tillsammans, med scenarier där misstag lätt uppstår.

## Vanliga misstag

Nybörjare kan göra följande misstag.

```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()
}
```

Om man antar att Go följer traditionellt arv verkar denna kod vara utan problem. Dess utdata är dock följande.

```
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
```

Här blir Gos beteende endast tydligt med följande kod.

```go
watermelon := apple
```

Denna kod omvandlar inte Apple till en Watermelon-klass. Variabeln watermelon är helt enkelt en pekare till apple.

Låt oss betona igen: **Go följer inte det traditionella arvsbegreppet.**

Om man skriver kod med denna missuppfattning uppstår allvarliga fel, såsom meningslösa pekarskapelser och oväntat kopierade funktioner för andra structar.

Hur ser då en mustergill kod ut?

## Lämpligt exempel i 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")
}
```

Go gör dock det möjligt att få det att **se ut som** arv.
Det handlar om anonym inbäddning (embedding). Detta sker genom att deklarera den inre structen utan namn.
I detta fall går det att komma åt fälten i den underordnade structen direkt, **utan att uttryckligen ange dem**.
Genom att använda mönstret att **lyfta upp** (promote) fälten från den underordnade structen till den överordnade kan läsbarheten förbättras i vissa fall. Om den underordnade structen måste synas explicit rekommenderas dock att inte använda detta.

```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")
}
```

I detta exempel finns följande skillnad.

```go
w.PrintAll() // Anropas automatiskt via den anonyma structen genom promotion, i stället för w.Fruit.PrintAll()
```

Båda exemplen har följande viktiga poänger:

- Håll main enkel och dela upp funktionerna efter funktionalitet
- Använd olika objekt för olika structar
- Använd en intern struct när delning krävs

Vilka fördelar har denna programmeringsfilosofi?

## Fördelar

- Tydlig åtskillnad mellan metoder som behöver delas och de som inte gör det
- Ansvarsfördelning mellan enskilda structar och metoder
- Kod som är strukturellt separerad efter specificerade funktionskrav

Till en början kan Go kännas ovant eftersom det skiljer sig från traditionell OOP, men när man vant sig blir det möjligt att programmera explicit.

## Sammanfattning

- Isolera ansvarsområden
- Dela upp i detalj per struct
- Metoder ska inte förstås som abstrakta klasser i Java
- Programmera explicit och konkret
  Go är enklare och mer okomplicerat än traditionella OOP-modeller och bör hanteras individuellt. Snarare än att programmera för utvidgning, låt oss skriva kod med stegvis och strukturerad separation.
