概要
A Tour of Goの順番に沿ってGoの基本で個人的に学習したことをまとめています。
No | 記事 |
---|---|
1 | 【Go】基本文法①(基礎) |
2 | 【Go】基本文法②(フロー制御文) |
3 | 【Go】基本文法③(ポインタ・構造体) |
4 | 【Go】基本文法④(配列・スライス) |
5 | 【Go】基本文法⑤(Maps・ Range) |
6 | 〜〜【Go】基本文法⑥(インターフェース)「今ココ」〜〜 |
7 | 【Go】基本文法⑦(並行処理) |
8 | 【Go】基本文法総まとめ |
今回は Interface
に焦点を当てて学習しました。
Interface(インターフェース)とは?
Interface(インターフェース)
とはメソッドの型だけを定義した型のことです。
interface(インターフェース)
を利用することで、オブジェクト指向言語でいうところのポリモーフィズムと同様の機能を実現できます。
Interface(インターフェース)の定義方法
インターフェースの定義は以下のように記述します(メソッドは複数記述することができます)
インターフェースの定義の内容は、単なるメソッドリストです。
type 型名 interface {
メソッド名1(引数の型, ...) (返り値の型, ...)
.....
メソッド名N(引数の型, ...) (返り値の型, ...)
}
上記の様にメソッドを羅列していくことでインタフェースを実装(満た)します。
空インタフェース
Go言語には、全ての型と互換性を持っている interface{}型(空インターフェース)
というものが存在しています。
interface{}(空インターフェース)
は文字通りゼロ個のメソッドを指定されたインターフェース型のことなので、以下の様に定義する事ができます。
interface{}
以下の例の様に interface{}(空インターフェース)
で宣言した変数にはどんな型の値でも代入可能です。
var obj interface{}
obj = 0123 // int
obj = "String" // string
obj = []string{"Python", "Golang", "Ruby"} // slice
obj = func greetings(_ string) string { return "Hello World" } // function
型アサーション
全ての型と互換性を持っている interface{}
ですが、interface{}型
の引数で受け渡された値は、元の型の情報が欠落しています。
そこで、 型アサーション
は、インターフェースの値の基になる具体的な値を利用する手段を提供します。
型のアサーションには以下の構文を使用します。
value := <変数>.(<型>)
この文は、インターフェースの値 <変数>
が具体的な型 <型>
を保持し、基になる <型>
の値を変数 value
に代入することを主張します。
また、以下の様にする事で、1番目の変数には型アサーション成功時に実際の値が格納され、2番目の変数には型アサーションの成功の有無(true/false)が格納されます。
value, ok := <変数>.(<型>)
実際に以上の方法で型のアサーションが出来る事を確認します。
func main() {
var intface interface{} = "hello"
variable := intface.(string)
fmt.Println(variable) //=> hello
variable, ok := intface.(string)
fmt.Println(variable, ok) //=> hello true
float, ok := intface.(float64)
fmt.Println(float, ok) //=> 0 false
//格納失敗が、成功したかの有無を確かめるokが存在するのでエラーにはならない。
float = intface.(float64)
fmt.Println(float) //=> panic: interface conversion: interface {} is string, not float64
//成功したかの有無を確かめるokが存在しないのでエラーが発生する。
}
上記の例ではインターフェースは string
として型付けされているので、それと異なる float64
等を <型>
部分に書くとエラーが発生します。
(成功したかの有無を確かめるok(左辺に2番目の変数)が存在するのでエラーにはならない。)
型switch
データの型判定は switch
文でも行うことができます。Go 言語では、これを 型 switch
といいます。
以下の様に 型switch
を書く事ができます。
switch v := x.(type) {
case 型1: ... // v は型1 の値になる
case 型2: ... // v は型2 の値になる
...
default: ...
}
型switch
では上記の様に switch
の後ろに型アサーション v := x.(type)
を書き、case
に型を指定します。
以下で実際の使用例を確認してみましょう。
func do(i interface{}) {
switch variable := i.(type) {
case int:
fmt.Println(variable)
case string:
fmt.Println(variable)
default:
fmt.Println("Default")
}
}
func main() {
do(23) //=> 23
do("hello") //=> hello
do(true) //=> Default
}
上記の様に書く事で、データの型判定を行いより柔軟に型のアサーションを実現する事が可能です。
構造体にインターフェースの実装
以下の様に書く事で構造体にインターフェースを実装する事ができます。
func (引数 構造体名) 関数名(){
関数の中身
}
実際に以下のコードから上記の方法でインターフェースを実装できることを確認します。
type People interface {
intro()
}
type Person struct {
name string
}
//構造体PersonがインターフェースPeopleを実装した事を意味します。
func (arg Person) intro(){
fmt.Println(arg.name)
}
func main() {
bob := Person{"Bob"}
bob.intro() //=> Bob
}
Interface(インターフェース)実例
以下のコードから Interface(インターフェース)
とは何か、より具体的に捉えます。
以下の例では Person
と Person2
という構造体があり、各構造体に intro()
メソッドが定義されており、各構造体が intro()
を実行するための IntroForPerson()
IntroForPerson2()
が存在しています。
type Person struct {} //Person構造体
type Person2 struct {} //Person2構造体
//Person構造体のメソッドintro()
func (p Person) intro() string{
return "Hello World"
}
//Person2構造体のメソッドintro()
func (p Person2) intro() string{
return "Hello World"
}
//Person構造体のメソッドintro()を実行するメソッド
func IntroForPerson(arg *Person){
fmt.Println(arg.intro())
}
//Person2構造体のメソッドintro()を実行するメソッド
func IntroForPerson2(arg *Person2){
fmt.Println(arg.intro())
}
func main(){
bob := new(Person)
mike := new(Person2)
IntroForPerson(bob) //=> Hello World
IntroForPerson2(mike) //=> Hello World
}
このままでは同様の動きをするIntroForPerson
とIntroForPerson2
メソッドが存在しており冗長です。
そこで以下のコードの様に Interface(インターフェース)
に括り出してみましょう。
type Person struct {} //Person構造体
type Person2 struct {} //Person2構造体
type People interface{
intro()
}
func IntroForPerson(arg People) {
arg.intro();
}
//Person構造体のメソッドintro()
func (p *Person) intro() {
fmt.Println("Hello World")
}
//Person2構造体のメソッドintro()
func (p *Person2) intro() {
fmt.Println("Hello World")
}
func main(){
bob := new(Person)
mike := new(Person2)
IntroForPerson(bob) //=> Hello World
IntroForPerson(mike) //=> Hello World
}
これによって関数をまとめる事ができ、より簡潔なコードとなりました。
参考
My Journey of Go⑥
インタフェースの実装パターン #golang
Go言語 - interfaceを触ってみる
はじめてのGo言語(インターフェース)
お気楽 Go 言語プログラミング入門
A Tour of Go
覚えたら書く