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バリバリ書いていけるようになりたいなぁと思ってたりします。