言語
日本語
English

Caution

お使いのブラウザはJavaScriptが無効になっております。
当サイトでは検索などの処理にJavaScriptを使用しています。
より快適にご利用頂くため、JavaScriptを有効にしたうえで当サイトを閲覧することをお勧めいたします。

  1. トップページ
  2. Go辞典
  3. interface

interface

対応: Go 1.0(2012)

インターフェースは、型が持つべきメソッドの集合を定義します。Goのインターフェースは暗黙的に実装され、明示的な宣言は不要です。

構文

// インターフェースを定義する
type インターフェース名 interface {
    メソッド名(引数) 戻り値型
}

// 空インターフェース(any)はすべての型を受け入れる
var v any = "Hello"
var v interface{} = 42

インターフェース一覧

構文・型概要
type I interface { M() }メソッド『M()』を要求するインターフェースを定義します。
any『interface{}』の組み込みエイリアスです(Go 1.18+)。すべての型を受け入れます。
interface{}メソッドが0個の空インターフェースです。すべての型が実装しています。
型アサーション: v.(T)インターフェース値を具体的な型に変換します。変換に失敗するとパニックが発生します。
型アサーション: v, ok := v.(T)変換に失敗しても『ok』が『false』になるだけでパニックは発生しません。
型スイッチ: switch v.(type)インターフェース値の具体的な型によって処理を分岐させます。

サンプルコード

interface.go
package main

import (
    "fmt"
    "math"
)

// Shapeインターフェースを定義する
type Shape interface {
    Area() float64
    Perimeter() float64
}

// Circle構造体を定義する
type Circle struct {
    Radius float64
}

// CircleがShapeインターフェースを暗黙的に実装する
func (c Circle) Area() float64 {
    return math.Pi * c.Radius * c.Radius
}

func (c Circle) Perimeter() float64 {
    return 2 * math.Pi * c.Radius
}

// Rectangle構造体を定義する
type Rectangle struct {
    Width, Height float64
}

// RectangleもShapeインターフェースを実装する
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

func (r Rectangle) Perimeter() float64 {
    return 2 * (r.Width + r.Height)
}

// インターフェース型を引数に取る関数
func printShape(s Shape) {
    fmt.Printf("面積: %.2f, 周囲: %.2f\n", s.Area(), s.Perimeter())
}

func main() {
    c := Circle{Radius: 5}
    r := Rectangle{Width: 3, Height: 4}

    printShape(c) // CircleはShapeを満たす
    printShape(r) // RectangleもShapeを満たす

    // 空インターフェースはすべての型を受け入れる
    var anything any = "文字列"
    fmt.Println(anything) // 『文字列』と出力される
    anything = 123
    fmt.Println(anything) // 『123』と出力される

    // 型アサーションで具体的な型に変換する
    var s Shape = Circle{Radius: 3}
    if circle, ok := s.(Circle); ok {
        fmt.Println("半径:", circle.Radius) // 『半径: 3』と出力される
    }

    // 型スイッチで型ごとに処理を分岐する
    values := []any{42, "hello", true, 3.14}
    for _, v := range values {
        switch val := v.(type) {
        case int:
            fmt.Printf("整数: %d\n", val)
        case string:
            fmt.Printf("文字列: %s\n", val)
        default:
            fmt.Printf("その他: %v\n", val)
        }
    }
}
go run interface.go
面積: 78.54, 周囲: 31.42
面積: 12.00, 周囲: 14.00
文字列
123
半径: 3
整数: 42
文字列: hello
その他: true
その他: 3.14

暗黙的実装の仕組み

Goのインターフェースは暗黙的に実装されます。JavaやKotlinのように『implements Interface』と宣言する必要はなく、インターフェースが要求するメソッドをすべて定義するだけで自動的にそのインターフェースを満たします。これを「ダックタイピング」と呼びます。

この仕組みにより、インターフェースの定義と実装を別々のパッケージに置けます。既存の型に後からインターフェースを当てはめることもできます。

sample_implicit_interface.go
package main

import "fmt"

// Stringer インターフェース(fmt パッケージにも同名のものがあります)
type Stringer interface {
    String() string
}

// 既存の型に後からインターフェースを適用できます
// - この構造体を定義したときに Stringer を意識する必要はありません
type Character struct {
    Name  string
    Power int
}

// Character に String() を定義すると Stringer を自動的に満たします
func (c Character) String() string {
    return fmt.Sprintf("%s (Power: %d)", c.Name, c.Power)
}

// Stringer を満たすものは何でも受け取れます
func printAll(items []Stringer) {
    for _, item := range items {
        fmt.Println(item.String())
    }
}

// コンパイル時にインターフェース実装を確認する慣用イディオム
// var _ Stringer = Character{}  // Character が Stringer を満たさなければコンパイルエラー

func main() {
    chars := []Stringer{
        Character{Name: "Kiryu Kazuma", Power: 95},
        Character{Name: "Majima Goro", Power: 92},
    }
    printAll(chars)
}
go run sample_implicit_interface.go
Kiryu Kazuma (Power: 95)
Majima Goro (Power: 92)

空インターフェースの注意点

空インターフェース(『any』/ 『interface{}』)はすべての型を受け入れますが、使いすぎると型安全性が失われます。具体的なインターフェースを定義することでより型安全なコードになります。

あまりよくない: 何でも受け取れますが、型情報が失われます。

sample_any_bad.go
package main

import "fmt"

func printAnything(v any) {
    fmt.Printf("値: %v, 型: %T\n", v, v)
}

func main() {
    var v any = "Kiryu"
    if s, ok := v.(string); ok {
        fmt.Println("文字列:", s)
    }

    if n, ok := v.(int); ok {
        fmt.Println("整数:", n)
    } else {
        fmt.Println("int ではありません")
    }
}
go run sample_any_bad.go
文字列: Kiryu
int ではありません

よりよい: 必要なメソッドだけを要求するインターフェースを定義します。

sample_any_interface.go
package main

import "fmt"

type Displayable interface {
    Display() string
}

type Item struct {
    Name  string
    Price int
}

func (it Item) Display() string {
    return fmt.Sprintf("%s: ¥%d", it.Name, it.Price)
}

func printDisplayable(d Displayable) {
    fmt.Println(d.Display())
}

func main() {
    printDisplayable(Item{Name: "Kiryu's Suit", Price: 50000})
}
go run sample_any_interface.go
Kiryu's Suit: ¥50000

よくあるミス1: ポインタレシーバの制約

ポインタレシーバで定義したメソッドは、値型(非ポインタ)ではインターフェースを満たせません。

package main

type Animal interface {
    Sound() string
}

type Cat struct{ Name string }

// ポインタレシーバで定義: *Cat は Animal を満たすが Cat は満たしません
func (c *Cat) Sound() string { return "Meow" }

func makeSound(a Animal) {}

func main() {
    // makeSound(Cat{Name: "Tama"}) // コンパイルエラー: Cat は Animal を満たしません
}

OK: ポインタを渡せばインターフェースを満たします。

sample_interface_ok.go
package main

import "fmt"

type Animal interface {
    Sound() string
}

type Dog struct{ Name string }

func (d Dog) Sound() string { return "Woof" }

type Cat struct{ Name string }

func (c *Cat) Sound() string { return "Meow" }

func makeSound(a Animal) {
    fmt.Println(a.Sound())
}

func main() {
    // Dog は値レシーバなので Dog も *Dog も Animal を満たします
    makeSound(Dog{Name: "Hachiko"})

    // Cat はポインタレシーバなので *Cat だけが Animal を満たします
    makeSound(&Cat{Name: "Tama"}) // ポインタを渡す
}
go run sample_interface_ok.go
Woof
Meow

よくあるミス2: nilインターフェースの罠

nil インターフェースと nil を含むインターフェースは異なります。インターフェース変数が nil かどうか比較するとき、型情報を持つポインタを格納した場合は nil にはなりません。

sample_interface_nil.go
package main

import "fmt"

type Animal interface {
    Sound() string
}

type Dog struct{ Name string }

func (d *Dog) Sound() string { return "Woof" }

func main() {
    var a Animal = nil            // インターフェース自体が nil
    var d *Dog = nil              // 型情報はあるが値が nil の Dog ポインタ
    var a2 Animal = d             // インターフェースに nil ポインタを格納

    fmt.Println(a == nil)  // true:  a は完全に nil
    fmt.Println(a2 == nil) // false: a2 は型情報を持つため nil ではありません!
}
go run sample_interface_nil.go
true
false

概要

Goのインターフェースは他の言語と異なり、型がインターフェースを実装することを明示的に宣言しません。型が必要なメソッドをすべて実装していれば自動的にそのインターフェースを満たします。これを「ダックタイピング」と呼びます。ダックタイピングとは、明示的にインターフェースを宣言しなくても、必要なメソッドを持っていれば自動的にそのインターフェースを満たすとみなす仕組みです。

型アサーション『v.(T)』は変換に失敗するとパニックを引き起こします。安全に変換するには『v, ok := v.(T)』の2値形式を使用してください。

空インターフェース『any』(旧『interface{}』)はすべての型を格納できますが、使用時は型アサーションや型スイッチで具体的な型に変換する必要があります。具体的なインターフェースを定義することで型安全性を保てます。

記事の間違いや著作権の侵害等ございましたらお手数ですがまでご連絡頂ければ幸いです。