---
id: ebf46089667a6469b258b8e52e57571f
author: Yunjin Lee
title: 'Go インターフェースと構造体の埋め込み: クラス継承との違いと合成による設計'
description: Java/C++のクラス継承とGoのインターフェースにおける構造的型付け(structural typing)の違いを比較し、構造体の埋め込み(struct embedding)とメソッド委譲(method delegation)を用いた合成(Composition)パターンをコード例によって説明します。
language: ja
date: 2025-07-17T10:40:02.284435348Z
path: /blog/posts/go-interfaces-are-not-inheritance-zd04c1084
category: language
---

# 概要

Goのインターフェースは、同じ引数と戻り値を持つ関数を複数の構造体で容易に持たせることができますが、javaのextendsキーワードのように、その内部関数の動作を適切に拡張しオーバーライドする方式とは異なります。Goの構成的なコード再利用を正しく理解してこそ継承と混同しなくなりますが、最初から理論的に完璧な理解をするのは難しいです。間違いやすいシナリオとともに見ていきましょう。

## よくある間違い

初心者の方は次のような間違いをすることがあります。

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

このようなコードは、Goが伝統的な継承に従うと誤解していれば問題がないように見えます。しかし、その出力結果は次のとおりです。

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

ここで、Goの動作はただ明確になります。

```go
watermelon := apple
```

このコードは、Appleをそのまま Watermelon クラスに変換するものではまったくありません。
単にwatermelonはappleへのポインタにすぎません。

ここで改めて強調しますが、**Goは伝統的な継承の概念に従いません。**

このような誤解を抱えたままコードを書くと、無意味なポインタの生成、意図しない他の構造体のための関数コピーなどの致命的なエラーが生じます。

では、模範的なコードはどのようなものでしょうか？

## 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でも継承**のように**見せることは可能です。
無名埋め込み（匿名埋め込み）という例です。これは内部構造体を名前のない構造体として宣言すると可能になります。
この場合には、**下位構造体のフィールドを明示せずに使用しても**そのままアクセスが可能です。
このように下位構造体のフィールドを上位構造体へ**昇格**させるパターンを利用すると、場合によっては可読性の向上が可能です。ただし、下位構造体を明示的に示す必要がある場合には使用しないことを推奨します。

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

この例では、次のような違いがあります。

```go
w.PrintAll() // w.Friut.PrintAll()ではなく、名前のない構造体を通じた自動昇格呼び出し
```

両方の例に共通する重要な点は次のとおりです。

- mainは簡潔に、関数は機能別に
- 異なる構造体であれば異なるオブジェクトを
- 共有が必要な場合は内部構造体を使用

このようなプログラミング哲学にはどのような利点があるでしょうか？

## 利点

- 共有が必要なメソッドとそうでないものの区別が明確
- 個別の構造体、メソッドに責任の所在を分離
- 必要な機能仕様に応じて構造的に分離されたコード

最初はGo言語が伝統的なOOPと異なるため、馴染みがないかもしれませんが、慣れると明示的なプログラミングが可能になります。

## まとめ

- 責任の所在を孤立させよう
- 構造体単位で詳細に分けよう
- メソッドはJavaの抽象クラスのように理解してはならない
- 明示的で具体的なプログラミングをしよう
  Go言語は伝統的なOOPモデルよりシンプルかつ明快で、個別に扱われるべきです。拡張的にプログラミングするよりも、段階的かつ構造的に分離して記述するようにしましょう。
