【第十一回Go言語学習備忘録】インターフェースを実装する!

2月 12, 2022

どもです。連日の外出規制でお気に入りのシャツが寝巻きになっているsaisaiです。


今回もGo学習備忘録です。本日はインターフェースについて学習したのでまとめていきたいと思います。


最近学習内容の難易度がどんどん上がってきているので、気を引き締めたいところです。

インターフェースを利用してみる!

それではそっそくインターフェースについて見ていきたいと思います。インターフェースとは、メソッドのみを定義した型のことを指すようです。


やはり言葉だけではなかなか具体的に想像できないのがプログラミング言語、早速実際にコードにしていきたいと思います。

package main

import "fmt"

type Sample interface {
	Set()
}

type Initial struct {
	Num int
}

func (v Initial) Set() {
	fmt.Println(v.Num)
}

func main() {
	var origin Sample = Initial{10}
	origin.Set() //10
}

上記のコードの場合、インターフェースは以下のように定義しています。

type Sample interface {
	Set()
}

Structの定義方法と形は似ていますが、その中身はメソッドである点に注意が必要です。次にStructを定義し、インターフェースに当てはめていきます。

func main() {
	var origin Sample = Initial{10}
}

今回は変数"origin"をSampleインターフェース型と定義し、Struct"Initial"に10を入れた値を"origin"に代入します。しかし、このままではSampleインターフェース内のSetメソッドは定義されていませんので、以下のように定義します。

func (v Initial) Set() {
	fmt.Println(v.Num)
}

これでSetメソッドの定義も完了しました。では、値を出力してみます。

func main() {
	var origin Sample = Initial{10}
	origin.Set() //10
}

このようにStructの値を定義してSetメソッドを実行することができました!

ポインタを利用した値の変更

もう少しインターフェースについて深掘りしてみたいともいます。以下のようなコードを作成してみました。

package main

import "fmt"

type Score interface {
	Get() int
}

type Point struct {
	Num int
}

func (v *Point) Get() int {
	v.Num = v.Num + 50
	return v.Num
}

func Result(v Score) {
	if v.Get() > 100 {
		fmt.Println("Pass")
	} else {
		fmt.Println("Failure")
	}
}

func main() {
	var high Score = &Point{100}
	var low Score = &Point{50}
	Result(high) //Pass
	Result(low) //Failure
}

前回学習したポインタレシーバのように一度定義したメソッドの値を変更してみました。まずは、インターフェースとStructの定義です。

type Score interface {
	Get() int //型を指定する
}

type Point struct {
	Num int
}

今回はメソッドに返り値を指定する必要があるので、インターフェース内のメソッドに型を定義する必要があります。
次に、Structに入れる値を2種類用意しそれぞれに対応する変数を定義、型をScoreインターフェース型とします。

func main() {
	var high Score = &Point{100}
  var low Score = &Point{50}
}

今回の定義で注目すべきはやはり"&"でしょう。メソッドの値はポインタを利用して変更するため、変数に代入する値もStruct"Point"のアドレス値でなければなりません。

次に、メソッドの値を変更してみましょう。

func (v *Point) Get() int {
	v.Num = v.Num + 50
	return v.Num
}

本来Getメソッドの値は、Struct"Point"の値がそのまま代入されるようになっていますが、上記のように定義することで"Structの値 + 50″がGetメソッドの値として定義されるようになりました。では、この再定義されたGetメソッドの値を引数とした関数を作成してみましょう。

func Result(v Score) {
	if v.Get() > 100 {
		fmt.Println("Pass")
	} else {
		fmt.Println("Failur")
	}
}

先ほど再定義されたGetメソッドの値を基準に条件分岐し、処理を実行します。定義した関数の呼び出し方は以下の通りです。

Result(high) //Pass
Result(low) //Failure

希望した値が出力されました。

タイプアサーション

これまではインターフェースの中にメソッドを定義し使用しました。しかし、インターフェースは中に何も指定せずに使用することもあります。

このような"空インターフェース"にはどんな型も入れることができます。下記のコードを見てみましょう。

package main

import "fmt"

func any(x interface{}) {
	switch v := x.(type) {
	case int:
		fmt.Println(v * 10)
	case string:
		fmt.Printf("this is %v\n", v)
	default:
		fmt.Printf("%T型\n", v)
	}
}

func main() {
	any(5) //50
	any("test") //this is test
	any(false) //bool型
}

このように引数の型によって処理を変更する関数を作成することができます。今回は空インターフェースに引数の型を渡し、インターフェースの中身によってcase分岐が行われるswitch分を作成しました。上記のコードの要となるのは

switch v := x.(type)

の部分です。空インターフェースに型情報が格納されても、あくまでインターフェース型であることに変わりはありません。そこで、以下のようにインターフェースに格納された型を明示的に宣言してあげる必要があります。

変数.(型)

と言う風に定義しなければなりません。これをタイプアサーションと言います。例えば空インターフェースにint型を入れた場合は

変数.(int)

といったようになります。さらに、今回のコードのように特定の型を宣言しない場合は

変数.(type)

という風に宣言します。こちらはタイプSwitchと呼ばれるようです。

ひとこと

ということで今回はインターフェースについて学習したことをまとめてみました。考え方が非常に抽象的で僕自身まだまだ理解し切れていないのが現状です。


どんどん使って身に染み込ませていきたいと思います。


ここまで読んでいただきありがとうございました!


-saisai-


↓本日のオススメ

Golang

Posted by CY