Golangのインターフェース(interface)の使い方を詳しく解説します

Golang

Golangはインターフェイスをサポートしており、暗黙のうちに実装されています。

また、インターフェイスを使用することでGolnagにおけるポリモーフィズムを可能にします。

この記事では、インターフェイスについて、インターフェイスとは何か、どのように使用できるかを説明します。

インターフェース(interface)とは?

インターフェイスとは、Goのポリモーフィズムを実現する抽象的な概念であり、インターフェースの変数は、その型を実装した値を保持することができます。

型アサーションは基礎となる具体的な値を取得するために使用されます。

Golangでインターフェイスを定義・宣言する方法

インターフェースは型として宣言されます。

以下は、インターフェイスを宣言する時の基本的な書き方です。

type インターフェース名 interface {}

インターフェース(interface)のゼロ値

インターフェースのゼロ値はnilです。つまり、何の値も型も持たないということです。

以下がインターフェースのゼロ値を出力しているコードになります。

package main

import (
	"fmt"
)

func main() {
	var testInterface interface{}

	fmt.Println(testInterface) // <nil>
}

Golangの空のインターフェイス

関数を全く持たないインターフェースは空でであり、空のインターフェースはどんな型でも保持します。

そのため、からのインターフェースは汎用的で非常に便利です。

以下が、空のインターフェースを定義する書き方です。

var testInterface interface {}

Golangでインターフェイスを実装する

インターフェイスは、そのインターフェイスの関数を型が実装している場合に実装されます。

以下では,インターフェイスを実装する方法を示す例を示します.

package main

import (
	"fmt"
)

type Person interface {
	greet() string
}

type Human struct {
	Name string
}

func (human *Human) greet() string {
	return "Hi, I am " + human.Name
}

func isHuman(human Person) {
	fmt.Println(human.greet())
}

func main() {
	var tanaka = Human{"Tanaka"}

	fmt.Println(tanaka.greet()) // Hi, I am Tanaka
	isHuman(&tanaka)            // Hi, I am Tanaka
}

Goで複数のインターフェイスを実装する

複数のインターフェースを同時に適応することもできます。

もしすべての関数が実装されていれば、その型はすべてのインターフェースを実装していることになります。

以下のBird型(構造体)は,関数をfly関数とwalk関数を実装することで両方のインタフェース(Flyer型・Walker型)を実装しています。

package main

import (
	"fmt"
)

type Flyer interface {
	fly() string
}

type Walker interface {
	walk() string
}

type Bird struct {
	Name string
}

func (b *Bird) fly() string {
	return "飛びます"
}

func (b *Bird) walk() string {
	return "歩きます"
}

func main() {
	var bird = Bird{"piyo"}

	fmt.Println(bird.fly())  // 飛びます
	fmt.Println(bird.walk()) // 歩きます
}

インターフェース同士を合成する

インターフェースは一緒に構成することができます。この構成は、ソフトウェア開発において最も重要な概念の一つです。

複数のインタフェースが実装されている場合、その型は合成を行ったことになり、ポリモーフィズムが必要な場合に非常に有効です。

インターフェースにおける値

インターフェース値には、具象型と動的型があります。

package main

import (
	"fmt"
)

type Flyer interface {
	fly() string
}

type Walker interface {
	walk() string
}

type Bird struct {
	Name string
}

func (bird *Bird) fly() string {
	return "飛びます"
}

func (bird *Bird) walk() string {
	return "歩きます"
}

func main() {
	var bird = Bird{"piyo"}
	
	fmt.Printf("%v: %T", b, b) // {piyo}: main.Bird
}

上記のコードの変数birdはBird型だが、中身の具体的な値は{piyo}です。

インターフェイスを使った型アサーション

型注釈(型アサーション)は、インターフェイスが保持する基本的な値を取得する方法です。

つまり、インターフェース変数に文字列が代入される場合、その変数が保持する基礎的な値は文字列であるということです。

以下は、インターフェイスを使った型アサーションの使い方を示すコード例です

package main

import (
	"fmt"
)

type Test struct {
	s string
	i int
}

func main() {
	var i interface{} = Test{"Hello, world", 100}

	fmt.Println(i)          //{Hello, world 100}
	fmt.Println(i.(Test))   //{Hello, world 100}
	fmt.Println(i.(Test).s) //Hello, world
	fmt.Println(i.(Test).i) //100
}

インターフェイスを使った型switch

型switchは、switch caseと極めて類似した制御構造であるが、唯一の違いは異なる条件を切り替えるためにインターフェース型を使用することです。

package main

import (
	"fmt"
)

func checkType(i interface{}) {
	switch i.(type) { // switchはインターフェイスの型を使用する
	case int:
		fmt.Println("Int")
	case string:
		fmt.Println("String")
	default:
		fmt.Println("Other")
	}
}

func main() {
	var i1 interface{} = "文字列です"
	var i2 interface{} = 100
	var i3 interface{} = true

	checkType(i1)  // String
	checkType(i2)  // Int
	checkType(i3)  //Other
}

インターフェース値の等価性

以下に示す条件のいずれかを満たす場合、インターフェース値は等しくなります。

  • 両方ともnilである
  • 両者とも同じ基礎となる具象値および同じ動的型を持っている

以下のコードでは、2つのインターフェース型の値を比較して等しいか確かめるisEqual関数を定義してます。

package main

import (
	"fmt"
)

func isEqual(i interface{}, j interface{}) {
	if i == j {
		fmt.Println("等しい")
	} else {
		fmt.Println("異なる")
	}
}

func main() {
	var i1 interface{}
	var i2 interface{}

	isEqual(i1, i2) // 等しい

	var i3 interface{} = 100
	var i4 interface{} = 100

	isEqual(i3, i4) // 等しい
}

関数でインターフェースを使う

インターフェースは、他の型と同じように関数に渡すことができます。インターフェイスを使用する際の大きな利点は、どんなタイプの引数でも使用できることです。

以下は、関数の引数においてインターフェイスの使い方を示すコード例です。

package main

import (
	"fmt"
)

func sample(i interface{}) {
	fmt.Printf("%T\n", i)
}

func main() {
	var a interface{} = "文字列です"
	var c int = 100

	sample(a) // string
	sample(c) // int
}

まとめ

インターフェイスは、Golangにおけるポリモーフィズムを実現することができます。

複数の型を渡すことができる関数では、インターフェイスを使用することができますし、素晴らしい機能なので、上手く使いこなしていきましょう。

タイトルとURLをコピーしました