Help us understand the problem. What is going on with this article?

Golang速習: interface{}型とinterface

More than 1 year has passed since last update.

Golangを始めてまず個人的にinterface{}型とinterfaceがごっちゃになりました。
この部分について、それぞれの特徴と使い方について整理できればと思います。
内容としては、自分の理解用に参考文献の内容を咀嚼してまとめたものになります。

TL;DR

  • interface{}型 -> どんな型も格納できる特殊な型・型チェックや型変換などに使える
  • interface -> type structの下に関数群を紐付ける書き方。オブジェクト指向でないGoにおいてclassに近しいことができる(ただし継承などはできない)
  • 2つは本質には同じもので実装するためのメソッド(中身)のない型の使い方に過ぎない、が個人的には分けて考えたほうが考えやすかった

interface{}型

interface{}型はint, string, boolなどと同じ、golangの型名です。{}の部分まで含めて型名です。
golangは静的型付け言語のため、本来であれば一つの型で複数の型しかとれないのですが、interface{}型は下のようにどんな型でも受け付けます(ただし演算はできません)

var x, y interface{}

printf("%#v", x) // -> nil
x = 1
x = 2.1
y = []int{1,2,3}
y = "hello"
y = 2

// error
// z := x + y

これを使うことで例えば以下のような使い方ができます

型チェック

receive := 3
var x interface{}

// 型チェック
x = receive
if xi, ok := x.(int); ok{
    fmt.Println( xi * xi ) //=> 9
  }

// 型チェックによる分岐
switch xi := x.(type){
  case int:
    fmt.Println("int") 
  case string:
    fmt.Println("string")
  default:
    fmt.Println("other")

JSONなど

interface{}はどんな型も格納できるため、受け取りとしてどのようなものが来るか分からない場合に活用できます。
例えば、Go言語でJSONを扱うの例をそのまま使わせて頂くと

[
  {"id":1,"name":"taro","birthday":"08-16","vivid_info":{"color":"red","weapon":"Rang"}},
  {"id":2,"name":"aoi","birthday":"06-17","vivid_info":{"color":"blue","weapon":"Impact"}},
  {"id":3,"name":"wakaba","birthday":"05-22","vivid_info":{"color":"green","weapon":"Blade"}},
  {"id":4,"name":"himawari","birthday":"07-23","vivid_info":{"color":"yellow","weapon":"Collider"}},
  {"id":0,"name":"rei"}
]

jsonの場合、このように要素そのものがこない場合があります。しかし、unmarshalを用いることで以下のようにjsonの要素を抽出できます

// jsonデコード用の構造体
type Person struct {
    Id       int    `json:"id"`
    Name     string `json:"name"`
    Birthday string `json:"birthday"`
}

func main() {
    // JSONファイル読み込み
    bytes, err := ioutil.ReadFile("sample.json")
    if err != nil {
        log.Fatal(err)
    }

    // JSONデコード
    // できるだけPerson型にマッピングしようとする
    var persons []Person
    if err := json.Unmarshal(bytes, &persons); err != nil { 
        log.Fatal(err)
    }

    // デコードしたデータを表示
    // 中身の要素をそれぞれ確保する
    for _, p := range persons {
        fmt.Printf("%d : %s\n", p.Id, p.Name, p.Birthday)
    }
}

結果は下のようになります

1 : taro 08-16
2 : aoi 06-17
3 : wakaba 05-22
4 : himawari 07-23
0 : rei

Birthdayのように存在しない場合は""で初期化された結果が帰ってきます

interface

interfaceはinterface{}型とは全く違う概念になります。
interfaceは構造体を使ってクラスのように表現する方法となります。
急いで学ぶGo lang#6 インターフェイスの例で説明します。
まずはじめにinterfaceを定義します。このinterfaceはclassのようなものだと考えると何となく分かりやすいです。

// Carのinterface
// Carの機能が関数として入っている
type Car interface {
  run(int) string
  stop()
}

// MyCarの構造体を定義
// 構造体は頭大文字
type MyCar struct {
  name string 
  speed int
}

// MyCarの構造体の下位クラスのような位置づけになる
// この時、interfaceにあるものは全て実装しないといけない

// run
func (u *MyCar) run(speed int) string {
  u.speed = speed
  return strconv.Itoa(speed) + "kmで走ります"
}

// stop
func (u *MyCar) stop() {
  fmt.Println("停止します")
  u.speed = 0
}

// 変数の定義
// ポインタを返すこと
// MyCarの構造体を定義・ポインタを渡す 
myCar := &MyCar{
 name: "マイカー", 
 speed: 0
}

// MyCarの構造体を持ち、Carインターフェースを持つ変数の定義
var objCar Car = myCar
fmt.Println(objCar.run(50))
i.stop()

このようにすることで、実際にクラスのようにMyCarを定義し、Carのメソッドを呼び出して利用することができます

interface{}と interface

実は上記見てきた2つは何が違うかと言うと本質的には一緒です。interface{}とは元来、中身のない型を表します。intやfloatといったものも全ての型がinterface{}型とも考えることができます。そのため、上記でインターフェースとして内部的に定義してきたものは結局空のメソッドの中にメソッドを格納していってクラス的に考えた使い方になります。

このように、実は一緒のinterface{}はその柔軟性から様々な使い方ができるのですが、今回は用途ベースで分けて考えてみました。

まとめ

interface{}型とinterfaceの違いを説明しました。もしかしたら由来的には一緒のものなのですが、なんとなくinterfaceって色々ありごっちゃになってしまうので分けて用途を説明しました。

この辺をうまく使ってGolangバリバリ書いていけるようになりたいなぁと思ってたりします。

参考文献

Go言語でJSONを扱う

interface{} な変数を型が決まっている関数の引数にする

インタフェースの実装パターン #golang

急いで学ぶGo lang#6 インターフェイス

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away