Taka blog

プログラミングとか

[golang(Go言語)]interface入門

Go言語のinterface(インターフェース)の使い方を学ぶにあたって、構造体とメソッドの解説が必要になるので、合わせて記載しています。また、interfaceとinterface{}型は見た目が被っていてややこしいので、両者の違いも最後に説明します。

構造体とメソッド

構造体とは

構造体は、いくつかの変数を保持するデータのまとまりのことです。Javaで言うとフィールドをいくつか持っているクラス、がイメージに近いと思います。

//packageとimportは省略

// 定義
// 基本的に先頭は大文字。他packageから利用するため。
type Person struct{
	Age		int // 基本的に先頭は大文字。変数名.フィールド名でアクセスするため。
	Name		string
	MyNumber	string
}

func  main() {
	// p := Person{} または var p Person とすることもできる。
	p := Person{
		Age : 20,
	}

	// 構造体型の変数の宣言時に指定しなかったフィールドにはゼロ値が入る(ゼロ値はint型なら0、string型なら""の空文字)
	fmt.Println(p.Name) // 出力は""(空文字です)

	// 変数名.フィールド名で代入可能
	p.Name = "太郎"

	fmt.Println(p.Name) // 出力は"太郎"
}

コード例に説明も合わせました。イメージは掴めたでしょうか?

メソッドとは

Go言語には関数とメソッドという似たものが存在します。定義の違いは、レシーバーが無いのが関数、有るのがメソッドです。例えば上のコード例内のfunc main()は、レシーバーが無いので関数です。レシーバーとメソッドに関しては、下のコード例をご覧ください。

func main() {
	p := Person{
		Name : "太郎",
	}

	fmt.Println(p.getName) // "太郎"
}

// (h Person)部分がレシーバーの指定です。
// hの部分は何の文字でも良いです。宣言した変数名と合わせる必要はありません。
// Personの部分には構造体名を指定します。
func (h Person)getName()(string) {
	// レシーバーをメソッド内で利用できます。
	return h.Name
}

上記のように、メソッドによって、構造体に関する処理を定義することができます。。

インターフェース

本題のインターフェースです。

構造体はフィールドの集まりでしたが、インターフェースはメソッドの集まりです。

構造体とは定義の方法が大きく異なります。インターフェースで定義したものは構造体と同じように変数名 := インターフェース名 とはできません。

手順としては、

  1. interfaceの定義
  2. interfaceを実装する構造体の定義
  3. interfaceで定義したメソッドを、構造体をレシーバーにして全て実装
という3つが必要になります。分かりづらいと思うのでコードを見てください。

// interfaceの定義
type PersonRepository interface{
	Get(myNumber string)(p Person)
}

// interfaceを実装したい構造体
type personRepository struct{
	// DBのコネクションなどを定義する
}

// personRepository型変数を他のpackageからこの関数で取得し、その変数に対するメソッドを利用します。
// 本番では引数にDBコネクションなどを引き渡して、それらをフィールドに持つpersonRepository構造体を返します。
func NewPersonRepository()(PersonRepository){
	return personRepository{}
}

// interface内のメソッドを実装します。interfaceで定義したメソッドと関数名・引数・戻り値が等しいメソッドを、全て用意しないとコンパイルエラーになります。
func (r personRepository)Get(myNumber string)(p Person){
	// DBから取得する処理を記述
}

interface{}

interface{}はintやstringなどの仲間で、データ型の一種です。

引数や戻り値でinterface{}型の変数を指定すると、どんな型の値でもその変数として受け渡しできます。型を指定されていた方がぱっと見分かりやすいので、使う機会は限られると思います。が、一応コード例を載せておきます。

func main() {
	s := test()

	// interface{}型変数名.(型名)で元の型の変数と成功可否を取れる
	g, isString := s.(string)
	if isString {
		fmt.Println(g)
	}
}

func test()(interface{}) {
	d :="太郎"
	return d
}