0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

# 【Go】チョットわかるインタフェース

Last updated at Posted at 2022-02-20

自分がインタフェースについて理解したことをまとめます。僕と同じ初学者の理解を手助けできればうれしいです。Gopher道場が提供してくれている動画やスライド、書籍「プログラミング言語Go」を参考にしています。

最初にこの記事の結論

以下のようになりました。タイトルの通りちょっとしかわかりませんでした。

インタフェースの使い方はちょっと分かったけど、メリットを実感できる使い方まではわからなかった。

はじめに用語を整理する

最初に用語を整理します。現時点は「へえ」くらいの温度感でいいと思うのでざっくり意味を理解することが大切だと思います。

メソッド レシーバと紐づけられた関数
インタフェース 抽象化を実現するメソッドの集まり

前段階にメソッドの理解を深める

本題はインタフェースの理解を深めることですが、前段階としてメソッドの理解を深める必要があると感じています。以下はメソッドを使用した簡単なコードです。一緒に見ていきましょう。

package main

import (
	"fmt"
)

type Hoge string

func (h Hoge) String() string {
	return string(h)
}

func main() {
	var hoge Hoge = "Hello World"
	fmt.Println(hoge.String())
}

https://go.dev/play/p/_Gs0R88968T

ざっくり解説すると、 Hoge 型の変数 hoge に”Hello World”を代入してレシーバ h が”Hello World”を受け取り、メソッド String() を呼び出し戻り値の String(h)Printlnの引数に渡しています。

とりあえず動くコードを書きましたが、メソッドの戻り値が以下でないことに疑問を持ちます。「 h は文字列の”Hello World”を受け取るからstring型では?文字列をstring型でキャストするって無意味では?」と思いました。

return h

https://go.dev/play/p/j8u6Rk2zE0s

しかし実行するとエラーになります。cannot use h (type Hoge) as type string in return argumentとメッセージがでて、h はstring型ではなく Hoge 型ですと注意されます。「なるほど、だから h はキャストして string(h) とする必要があるのか」と理解できました。

本題のインタフェースを理解していく

続いて本題のインタフェースに触れていきます。先ほどと同様にコードを一緒に見ていきましょう。

package main

import "fmt"

type Hoger interface {
	String() string
}

type Hoge string

func (h Hoge) String() string {
	return string(h)
}

func main() {
	var hoge Hoger = Hoge("Hello World")
	fmt.Println(hoge.String())
}

https://go.dev/play/p/M6rAuEbm4rb

このインタフェースを使用したコードは、Hoge はインタフェース Hoger を実装していると言えます。様々な場面でこの表現を耳にします。しかし僕はこの言葉がすんなり入ってきませんでした。僕と同じような感覚の方は多いのかな?と思っています。僕が思うにここで詰まる原因はインタフェースの概念について理解が足りてないのかなと思います。なので一緒に一つずつ理解していきましょう。

☆少し脱線 | 少し概念を理解する抽象的な話

ここからは概念について抽象的な話をしていきます。以下はメソッドのイメージ図を書いてみました。通常は関係者同士でやり取りをします。

image.png

しかしインタフェースを利用すると少し異なります。以下fig2はインタフェースを使用したイメージ図です。直接やり取りをせずインタフェースを介して必要な情報を渡します。これにより抽象化を実現できます。抽象化を実現することがインタフェースの役割と理解しています。

image.png

インタフェースを介することにどのようなメリットがあるかというと、①結果が欲しい人は②結果を返す人を意識する必要がないことです。僕のイメージ的には**インタフェースは「上手い感じに取り次いでくれる優しい人」**です。これにより①結果が欲しい人は、「良しなにお願いしますね。」とできます。これがインタフェースを利用する目的であり、インタフェースが必要な理由だと理解しています。

☆話を戻します

話を戻して先ほどのコードを一緒に見ていきましょう。インタフェースを実装することで追加した箇所、変更した箇所に注目していきます。

https://go.dev/play/p/M6rAuEbm4rb

type Hoger interface {
	String() string
}

ここでは、インタフェースはメソッドの集まりと認識することが大切だと感じます。**『型 Hoge はインタフェース Hoger を実装している』**は以下fig3に注目すると理解が進む気がします。ここで定義されているメソッドのレシーバ hHoge 型です。

image.png

ただしインタフェースを実装することで恩恵を受ける(言い換えるとメリットがある)のは①結果が欲しい人となります。ここがモヤっとする箇所な気がします。

次はレシーバ h で”Hello World”を受け取る箇所の書き方が異なります。

	var hoge Hoger = Hoge("Hello World")

ぱっと見、Hoge メソッドに文字列を渡していると感じませんか?僕が勘違いしていた。。。正確には”Hello World”を Hoge 型にキャストしています。(そもそも Hoge メソッドなんてありませんよね。)しかしここで疑問が生じます。「 Hoge 型でキャストするってどうゆうこと?」と思いました。なのでキャストを解除してみました。

var hoge Hoger = "Hello World"

cannot use "Hello World" (type string) as type Hoger in assignment:とエラーメッセージが出ます。”Hello World”は Hoger 型ではありませんと注意されます。わかりましたと。いまれた通り Hoger でキャストしてあげます。しかし次は別のエラーが出ます。

var hoge Hoger = Hoger("Hello World")

cannot convert "Hello World" (type string) to type Hoger: とメッセージは、 Hoger 型にキャストできませんと言われます。は?注意された通り直したよね?となりましたが、実はスルーしていたエラーメッセージの2行目を見てみます。string does not implement Hoger (missing String method) と出ています。DeepLの翻訳をそのまま書くと、「string が Hoger を実装していない(String メソッドがない)。」となります。

少し立ち返ると、ここではレシーバ h に”Hello World”を渡したいのです。なのでレシーバの型である Hoge でキャストしてあげる必要がありました。**『型 Hoge はインタフェース Hoger を実装している』**ので Hoger 型を Hoge 型にキャストすることが可能です。

なんとなくわかったけど結局、、、うーんわからん

ここまで来てまたひとつ疑問が生じました。「結局①結果が欲しい人のメリットは何?」と思いました。コードを色々いじってみます。

変数 hoge で複数のメソッドを呼ぶことができる

package main

import "fmt"

type Hoger interface {
	String() string
	Fuga() string
}

type Hoge string

func (h Hoge) String() string {
	return string(h)
}

func (h Hoge) Fuga() string {
	return string(h)
}

func main() {
	var hoge Hoger = Hoge("Hello World")
	fmt.Println(hoge.String())
	fmt.Println(hoge.Fuga())
}

https://go.dev/play/p/fYVTr1A_sf3

このようにインタフェースにメソッド Fuga() を追加することで変数 hoge でメソッドを呼ぶことができます。でも正直、便利なのか?という感覚になりました。感覚的にはインタフェースの役割が伝書鳩(ただ取り次ぐだけで価値を提供しない人)に感じます。

構造体(コンポジット型)をレシーバ h に受け渡す

package main

import "fmt"

type Hoger interface {
	String() string
	Fuga() string
	Piyo() int
}

type Hoge struct {
	A string
	B string
	C int
}

func (h Hoge) String() string {
	return string(h.A)
}

func (h Hoge) Fuga() string {
	return string(h.B)
}

func (h Hoge) Piyo() int {
	return int(h.C)
}

func main() {
	var hoge Hoger = Hoge{A: "Hello World", B: "ennyu", C: 123}
	fmt.Println(hoge.String())
	fmt.Println(hoge.Fuga())
	fmt.Println(hoge.Piyo())
}

https://go.dev/play/p/Bc1BgXXroEd

これは少し便利かな?と思いましたが、関数で良くない?となりました。

package main

import "fmt"

type Hoge struct {
	A string
	B string
	C int
}

func String(ret Hoge) string {
	return string(ret.A)
}

func main() {
	var hoge Hoge = Hoge{A: "Hello World", B: "ennyu", C: 123}
	fmt.Println(String(hoge))
}

https://go.dev/play/p/MsnpqEyUUFQ

結論

インタフェースの使い方はちょっと分かったけど、メリットを実感できる使い方まではわからなかった。

まとめ

チョットだけ理解できたと思いますが、インタフェースを使いこなすまでの理解はできませんでした。ただコードを読むための力はついたはずなので、標準パッケージを読み込むなどで理解度を向上させて使いこなしたいです。

余談

わからない構文は自分で書いてみて試行錯誤するのが大切だなと感じました。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?