72
39

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Go】基本文法⑥(インターフェース)

Last updated at Posted at 2018-08-30

概要

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(インターフェース) とは何か、より具体的に捉えます。
以下の例では PersonPerson2という構造体があり、各構造体に 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
}

このままでは同様の動きをするIntroForPersonIntroForPerson2メソッドが存在しており冗長です。
そこで以下のコードの様に 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
覚えたら書く

72
39
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
72
39

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?