インターフェースとは
Go言語では、インターフェース(interface)は抽象的な型定義として使用されます。インターフェースはメソッドのシグネチャを定義しますが、具体的な実装は含まれません。特定のインターフェースを実装した任意の型は、そのインターフェースとして扱うことができます。これにより、異なる型を統一的な方法で処理でき、コードの柔軟性、拡張性、および保守性が向上します。
以下に、Shapeインターフェースとその実装例を示します。
type Shape interface {
Area() float64
}
// 円形の構造体
type Circle struct {
Radius float64
}
// 円形の面積計算メソッド
func (c Circle) Area() float64 {
return 3.14 * c.Radius * c.Radius
}
// 長方形の構造体
type Rectangle struct {
Length, Width float64
}
// 長方形の面積計算メソッド
func (r Rectangle) Area() float64 {
return r.Length * r.Width
}
// 形状の面積を表示する関数
func printArea(s Shape) {
fmt.Printf("面積: %.2f\n", s.Area())
}
func main() {
circle := Circle{Radius: 5}
rectangle := Rectangle{Length: 4, Width: 6}
printArea(circle)
printArea(rectangle)
}
この例では、Circle構造体とRectangle構造体はどちらもAreaメソッドを実装しているため、Shapeインターフェースとして扱うことができます。
インターフェースの内部データ構造
Go言語には主に2種類のインターフェースがあります。一つは空インターフェース(interface{})で、任意の型の値を保持できます。もう一つは非空インターフェースで、特定のメソッドシグネチャを定義し、そのメソッドを実装した型のみが対象となります。
空インターフェースと非空インターフェース
空インターフェースの内部データ構造はruntime.efaceです。
type eface struct {
_type *_type
data unsafe.Pointer
}
_typeフィールドは値の詳細な型情報を含む_type構造体を指し、dataフィールドは実際のデータのメモリアドレスを指すポインタです。
非空インターフェースの内部データ構造はruntime.ifaceです。
type iface struct {
tab *itab
data unsafe.Pointer
}
dataフィールドは実際のデータを指すポインタですが、tabフィールドが重要です。これはitab構造体を指します。
itab構造体
itab構造体はインターフェースの型情報とデータの型情報を含みます。
type itab struct {
inter *interfacetype
_type *_type
hash uint32 // copy of _type.hash. Used for type switches.
_ [4]byte
fun [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter.
}
type interfacetype struct {
typ _type // インターフェースの型
pkgpath name // インターフェースのパッケージパス
mhdr []imethod // インターフェースが定義するメソッドリスト
}
interフィールドはインターフェース自体の型情報を記述します。_typeフィールドは実際の値の型情報を保持します。hashフィールドは_type構造体のハッシュ値のコピーであり、型比較や変換などの操作で高速な判定を行うために使用されます。funフィールドは動的にサイズが変わる関数ポインタ配列で、fun[0]==0の場合、_typeはインターフェースを実装していないことを示します。実装されている場合、funは最初のインターフェースメソッドのアドレスを保持し、他のメソッドは順に格納されます。
_type構造体
_type構造体は、Goランタイムが任意の型に関する情報を保持するために使用されます。