feature image

2021年12月10日 | ブログ記事

Go(golang)のinterfaceを†完全理解†した

この記事はtraP Advent Calendar 2021 27日目の記事です。

この記事は「interface何も分からん」ってなっている人が、ざっくりinterfaceの気持ちを理解するために書かれています。よって正確性に欠けることがあります。

目次

1.はじめに
2.例
3.コードの解説
4.実行の流れ
5.この例を踏まえて
6.疑問
7.結論


あなたは誰? : いろり(@irori)。sysAd班,グラフィック班
できること : golang,mysql(諸説あり)
できないこと : フロントエンド全般


はじめに

golangのコードを読むとちょくちょくインターフェイスが出てきます。
ここでインターフェイスの理解が中途半端だと、コードの意味を見失ったりします(n敗)。
iroriは部内イベント管理サービスであるknoQのバックエンドを担当しているのですが、knoQ進捗会でのfuji先輩とwasabi先輩のinterfaceの説明がわかりやすかったので、残しておきたいと思います。


//Circle(円)という構造体を定義 *1
type Circle struct {
    Radius int
}

//Circleのメソッドを定義 *2
func (c Circle) GetArea() int {
    return 3 * c.Radius * c.Radius
}

//Square(正方形)という(ry 
type Square struct {
    Height int
}

func (s Square) GetArea() int {
    return s.Height * s.Height
}

//Figure(図形)インターフェイスを定義 *3
type Figure interface {
    GetArea() int
}

//Figureの面積を表示する関数を定義 *4
func DisplayArea(f Figure) {
    fmt.Printf("面積は%vです\n", f.GetArea())
}

//実行される部分 *5
func main() {
    circle := Circle{Radius: 1}
    DisplayArea(circle)
    
    square := Square{Height: 3}
    DisplayArea(square)
}

コードの解説

*1

circleという構造体を定義している。
構造体とクラスはほぼ同じと考えていいと思う。RPGの村人クラスだったら、年齢と性別があるのと同じように、円クラスは半径という数値を持つ(と定義している)。
(型と同様に考えても良いと思う。つまりint型、string型、circle型という感じ)

*2

円クラスのメソッドを定義している。これは円の面積を求めるメソッド。
メソッドは円クラスのもののみが使える関数というイメージでオッケー。

Square(正方形)も同様です。

*3

インターフェイスを定義している。
書き方はこう。

type 名前 interface {
    メソッド メソッドでreturnされる型
}

意味は「Figureというのは引数なし,返り値intのメソッドGetArea()を持つインターフェイスです」ということ。
「なぜメソッドなのか?」、「どういう意味があるのか?」は後述。

*4

Figureインターフェイスのみで使える関数を定義している。引数はインターフェイス。
(正確にはインターフェイスの条件を満たすクラスの何かです)

*5

goではファイルを実行した時に
func main(){}の {} の中が実行されます。(ド基礎。よく忘れる...)
つまりここが実行される場所。

circle := Circle{Radius: 1}

ではCircle型(クラスの意)のcircleが作られている。そしてそのcircleには半径1が代入されている。
(RPGを例に出すと村人型の村人Aが作られ、年齢15歳が代入されているイメージ。)

DisplayArea(circle)

ではcircleの面積が計算され、「面積は3です」と表示されます。

実行の流れ

前提

  1. circleはCircle型
  2. Circle型はメソッドGetArea()を持つ。よってFigureインターフェイスに含まれる
  3. Figureインターフェイスに含まれるのでDisplayArea()が実行できる

🆗

処理

circle := Circle{Radius: 1}

で半径1のcircleが作成される

  1. DisplayArea(circle)では、関数DisplayAreaが呼び出され
fmt.Printf("面積は%vです\n", circle.GetArea())

が実行される

circle.GetArea()

は(circleの半径が1だったから)面積の計算結果3とが返却される

fmt.Printf("面積は%vです\n", 3)

になるから「面積は3です」と表示される

この例を踏まえて

同じメソッドを持つ型に共通する関数を作りたいんだなー とわかればOK

つまりGetArea() がラベルのような働きをしている!

GetArea()を持つ型のみが関数DisplayArea を実行できるよ〜 ということ。

そして、「GetArea()を持つ」という条件をFigureインターフェイスといいます。(ちょっとざっくり言いすぎかもしれない)

疑問

何でメソッドをラベルに代わりにするんだ? ラベルみたいなものを直接定義すればよくないか?

つまり

Q.

ラベル@Figure が貼ってある型はDisplayArea を実行できる

みたいにした方がスマートだし、柔軟性が効くのでは?

A.

例えば@Figureがあり、@Figureがついている型に実行できる関数があったとしよう。
その時にその関数は何ができるだろうか?

@Figureは何らかのメソッドを持つことを保証するものではないので、@Figureの全てで 〜ができるという保証がない

逆に、GetArea()のメソッドを持つ型をFigureインターフェイスとすれば、Figureインターフェイスは少なくともGetArea()を使用できることが保証されている


つまり、

interfaceで呼び出された関数は,interfaceで定義されたメソッドを使うことを基本的に前提としている

interfaceは(意味由来)やりたいことから作られている

のである!

(とiroriは理解した)


次(今日)の担当はiroriさんです。お楽しみに〜(???)
明日の担当はryohaさんです。楽しみ〜

irori icon
この記事を書いた人
irori

SysAd班、アルゴリズム班(Kaggle)とグラフィック班で活動しています

この記事をシェア

このエントリーをはてなブックマークに追加
共有

関連する記事

2021年8月12日
CPCTFを支えたWebshell
mazrean icon mazrean
2021年5月19日
CPCTF2021を実現させたスコアサーバー
xxpoxx icon xxpoxx
2022年4月5日
アーキテクチャとディレクトリ構造
mazrean icon mazrean
2021年12月8日
C++ with JUCEでステレオパンを作ってみた【AdC2021 26日目】
liquid1224 icon liquid1224
2021年5月16日
CPCTFを支えたインフラ
mazrean icon mazrean
2022年8月13日
traQにOBからバグ報告が来た
logica icon logica
記事一覧 タグ一覧 Google アナリティクスについて