【Go】メソッドを理解する〜メソッド・ポインタレシーバ・インターフェース〜

Goを学んでいてメソッドが出てきたんですが、関数とメソッドの違いとかポインタレシーバとかembeddedとか、なんか整理できなくなってきたのでこちらでまとめたいと思います。

また、この記事ではメソッドと関数と言う言葉は明確に使い分けしています。
## メソッドと関数の違い
とりあえず、下のサンプルコードを見たあとで説明をしていきます。
**関数**

```go:関数
package main

import (
  "fmt"
)

type Vertex struct {
  X, Y int
}

func Calc(v Vertex) int {
  return v.X * v.Y
}

func main() {
  v := Vertex{10, 5}
  fmt.Println(Calc(v)) // => 50
}
```
**メソッド**

```go:メソッドのsyntax
func(レシーバ引数 型) 関数名(引数: option) 返り値: option {
  // コード
}
```

```go:メソッド
package main

import (
  "fmt"
)

type Vertex struct {
  X, Y int
}

func (v Vertex) Calc int {
  return v.X * v.Y
}

func main() {
  sample := Vertex{10, 5}
  fmt.Println(sample.Calc()) // => 50
}
```
**メソッド**とは、**レシーバ引数を伴う関数**のこと。
`func`と` 関数名`の間にレシーバ引数をとります。
上記の例では`(v Vertex)`がレシーバ引数で、`v`という名前の`Vertex`(構造体型)のレシーバ引数を持っていると言えます。

こうすることにより、`Calc`(関数)は`Vertex`(構造体型)に紐付けられて、`sample.Calc()`と呼び出すことができるわけです。

ちなみに、この紐付けられた構造体の方をレシーバと呼ぶみたいです。

逆に、**関数**は構造体に紐付けられていないもの、つまり**レシーバ引数を伴わない**もののことをいいます。

## 値レシーバとポインタレシーバ
### 値レシーバ
上記のメソッドのコードのレシーバは値レシーバと呼びます。

```go
func (v Vertex) Calc int {
  return v.X * v.Y
}
```
`(v Vertex)`は値レシーバ。
`(v *Vertex)`だとポインタレシーバ。

### ポインタレシーバ
```go
package main

import (
  "fmt"
)

type Vertex struct {
  X, Y int
}

// 値レシーバ
func (v Vertex) Calc int {
  return v.X * v.Y
}

// ポインタレシーバ
func (v *Vertex) Scale(i int) {
  v.X = v.X * i
  v.Y = v.Y * i
}

func main() {
  sample := Vertex{10, 5}
  fmt.Println(sample.Calc()) // => 50

  sample.Scale(10)
  fmt.Println(sample) // => {100 50}
  fmt.Println(sample.Calc()) // 5000
}
```
上記のコードを見てみると、`Scale`メソッドではレシーバが`(v *Vertex)`とポインタ型になっています。

#### 暗黙的に変換されている
値型(int型やstring型など)とポインタ型間の型変換をコンパイラが暗黙的に変換してくれるみたいです。

例えば、構造体型の`sample`がレシーバの型がポインタ型の`Scale`メソッドを問題なく呼び出しています。
これはコンパイラが構造体型をポインタ型に暗黙的変換を行ってくれているからです。

またメソッドが`(v *Vertex)`の様なポインタレシーバの場合は、`v.X = ~`の`v.X`部分は暗黙的に`(*v).X`として扱われます。
なので、`v.X = v.X * i`でも元の変数の値が変更されているわけです。

ここら辺は[こちらの記事](https://skatsuta.github.io/2015/12/29/value-receiver-pointer-receiver/)が非常に参考になりました！

### 値レシーバとポインタレシーバのまとめ
値レシーバは、メソッドで**元の変数のコピー**を操作していることになります。要するに、関数の引数と同じ振る舞いをすると言うことです。
なので、**メソッド内で値を変更**しても変更されるのはメソッド内でコピーされたものだけで、**元の変数は変更されません。**

しかし、ポインタレシーバの場合は、ポインタ型を渡しているのでメソッド内で値を変更したら、元の変数も変更されます。

（ポインタがよく分かっていないと言う方は[【Go】ポインタについてまとめてみた](https://qiita.com/marsstay0729/items/def8eea2d66fa616e60a)を読んでみてください。）

## インターフェース（interface）
以下のコードを使用してインターフェースを説明していきます。:point_down:

```go
package main

import (
  "fmt"
)

type Human interface {
  Greeting() string
}

type User struct {
  Name string
}

func (user User) Greeting() string {
  text := "私の名前は" + user.Name + "です。"
  return text
}

func main() {
  var hiroki Human = User{"宏樹"}
  fmt.Println(hiroki.Greeting()) // => 私の名前は宏樹です。
}
```
### 定義方法
インターフェースは**`type`**と**`interface`**を使用して定義します。

また、[Interfaces - A Tour of Go](https://go-tour-jp.appspot.com/methods/9)では
> interface(インタフェース)型は、メソッドのシグニチャの集まりで定義します。

と記載されています。
（シグネチャは状況によって色々な意味を持つ[みたい](https://developer.mozilla.org/ja/docs/Glossary/Signature)ですが、ここでの意味としては「メソッド名」「引数の型」「返り値の型」をシグネチャと呼ぶっぽいです。）

```go:定義
type interface名 interface {
  メソッド名(引数の型: option) 返り値の型
}
```
`type`を使用していることからも分かる通り、**インタフェース(interface)型という型の1つ**です。

上のコードでは

```go
type Human interface {
  Greeting() string
}
```
という部分がインターフェースの定義部分にあたります。

### 使い方
このインターフェースは型の1つなので変数を宣言する時に使うことができます。:point_down:

```go
// Humanというインターフェース型
var hiroki Human = User{"宏樹"}
```
これで変数`hiroki`は`Human`というインターフェース型の構造体となります。
このインターフェース型を使用した変数は、インターフェースで定義したメソッドを使用する必要があります。

つまりこの場合、変数`hiroki`には`Greeting`メソッドが存在しなければならないので、このままだとエラーが表示されます。
なので、この構造体`User`に`Greeting`メソッドを紐付けてあげれば良いわけです。:point_down:

```go
func (user User) Greeting() string {
  text := "私の名前は" + user.Name + "です。"
  return text
}
```
上の「メソッドと関数の違い」の章で書いた様に、こうすることで`Greeting`メソッドは`User`に紐付けられます。
これでエラーは表示されなくなり、`hiroki.Greeting()`という風な呼び出し方が可能になります。

### どんな型でも受け入れる
interfaceを使うとどんな型でも受け入れることができます。
例えば以下のコードは`do`という関数に`interface型`の引数と返り値を取っています。

```go
package main

import (
  "fmt"
)

// 引数と返り値の型はなんでもOK
func do(any interface{}) interface{} {
  return any
}

func main() {
  fmt.Println(do(100))    // => 100
  fmt.Println(do("test")) // => test
  fmt.Println(do(true))   // => true
}
```

#### 型アサーション
どうやら`interface型`はどんな型でも受け入れることができるみたいですが、引数で受け取った値は元の型を失うみたいです。
なので、そのまま文字列を連結したり、数値を計算する場合はエラーが発生します。

そこで使用するのが**型アサーション**です。
型アサーションとは、失った元の型をチェックしてくれる仕組みです。
わかりにくいかもしれないので実際のコードを見てみましょう。

```go:syntax
変数.(型)
```

```go
package main

import (
  "fmt"
)

// 型アサーション
func do(i interface{}) interface{} {
  return "私の名前は" + i.(string) + "です。"
}

func main() {
  fmt.Println(do("宏樹")) // => 私の名前は宏樹です。
}
```
`i`はインターフェース型で元の型を失っていましたが、`i.(string)`とすることによりstring型というチェックがされて正常に実行されます。
ちなみに、`do(100)`などの他の型を入れるとpanicエラーを起こします。

#### 型switch
上の例では、インターフェース型でどんな型でも受け取れたけど、結局string型の場合はstring型用の処理しかできませんでした。これだと普通に引数をstring型として受け取った方が良いと思います。

そこで使用するのが型switchというものです。
**型switch**を使用することにより、string型の場合はstring型用の処理、int型の場合はint型用の処理、という風に**型によって処理を分岐**することが可能になります。

```go:syntax
switch 変数名 := インターフェース型の変数.(type) {
 case 型:
   // 処理
 default:
   // 処理
}
```

```go
package main

import (
  "fmt"
)

func do(i interface{}) interface{} {
  // 型アサーションをしてvに代入
  switch v := i.(type) {
  case string:
    return "私の名前は" + v + "です。"
  case int:
    return v * 100
  default:
    return "その他の型です。"
  }
}

func main() {
  fmt.Println(do("宏樹")) // => 私の名前は宏樹です。
  fmt.Println(do(100))   // => 10000
  fmt.Println(do(false)) // => その他の型です。
}
```

### インターフェースのまとめ
- インターフェースは`type`と`interface`を使用することによって定義できる。
- 定義したインターフェース型を使用した構造体は、インターフェース内に存在するメソッドを持っていなければならない。（= 紐付いている必要がある。）
- どんな型でも受け入れることができる様になる
- 型アサーションを使って元の型の情報をチェックできる
- 型switchによって型ごとに処理を振り分けることができる

## 参考サイト
**メソッド**
[Methods - A Tour of Go](https://go-tour-jp.appspot.com/methods/1)
[関数とメソッドの違い【golang】](https://qiita.com/sukezane/items/5bd061b4a766c43c9995)

**値レシーバとポインタレシーバ**
[Go 言語の値レシーバとポインタレシーバ](https://skatsuta.github.io/2015/12/29/value-receiver-pointer-receiver/)

**インターフェース**
[【Goのやさしい記事】Goのインターフェースを10分で学ぼう](https://qiita.com/gold-kou/items/fdecea86fbe65e65160a)
[インタフェースの実装パターン #golang](https://qiita.com/tenntenn/items/eac962a49c56b2b15ee8)
[【Golang】Golangのinterfaceで知っておくとお得なTips](https://qiita.com/ume-kun1015/items/e49e28b7dcf645ac91c7)
[Signature (シグネチャ) - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN](https://developer.mozilla.org/ja/docs/Glossary/Signature)
[Go言語 - 空インターフェースと型アサーション](https://blog.y-yuki.net/entry/2017/05/08/000000)