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

Unionがない・・・どうすれば・・・

A. interface使う

具体例

golangはUnionを持たないのでPythonのコードで例を出します。

from typing import Union


class Dog:
    def bark(self):
        return "ワン"


class Cat:
    def bark(self):
        return "ニャー"


def makeBark(dog_or_cat: Union[Dog, Cat]):
    return dog_or_cat.bark()


dog = Dog()
cat = Cat()

print(makeBark(dog))  # ワン
print(makeBark(cat))  # ニャー

コードの解説はしません。ただUnionがあるので犬でも猫でも引数に取ることができます

これをUnionを持たない言語、つまりGolangではどの様に実現するかという話です。

答え

package main

type Dog struct{}

type Cat struct{}

type Barker interface {
    bark() string
}

func (dog Dog) bark() string {
    return "ワン"
}

func (cat Cat) bark() string {
    return "ニャー"
}

func MakeBark(b Barker) string {
    return b.bark()
}

func main() {
    dog := Dog{}
    cat := Cat{}
    println(MakeBark(dog))
    println(MakeBark(cat))

}

解説します

インターフェースとは

インターフェースとはポリモーフィズムの実現方法の一つです。これによりダックタイピングが可能になります。
↑はぁ...そうですか。

golangにおけるインターフェースとは、メソッドの抽象を定義する場所です。メソッドの抽象とは、関数の具体的な処理(関数の中身)を書くのではなく、「あるメソッドがどのような引数を受け取り、どのような返り値を返すのか」ということだけ書いておきます。つまり引数と返り値の型だけを書いておきます。

type Barker interface {
    bark() string
}

インターフェースBarkerbark()というメソッドの抽象を定義しています。

抽象だけ書いても意味がないので、具体的な処理を書いたメソッドを定義します。

ちょうどのこのように。

func (dog Dog) bark() string {
    return "ワン"
}

func (cat Cat) bark() string {
    return "ニャー"
}

Dog, Catそれぞれにbark()のメソッドを定義しました。
この時、Dog, CatはインターフェースBarkerを満たしています(インターフェースに書かれたメソッドを"全て"持っていることを満たすといいます)。

  • DogはインターフェースBarkerを満たしている
  • CatはインターフェースBarkerを満たしている

この時、Barker型の値を引数に取る関数を定義すると...?

このように。

func MakeBark(b Barker) string {
    return b.bark()
}

Barkerを満たす型はDog, Catの2つ。Unionを使うことなく、実質的に複数の型を取りえる関数の定義に成功しました。

良さがわからない?

interfaceを使うことで消えたものが何かを想像しましょう。つまりinterfaceを使わずに書いたコードから消えたものを引き算します。

インターフェースを使わずに書いたコード

package main

import "reflect"

type Dog struct{}

type Cat struct{}

func MakeBark(b interface{}) string {
    if reflect.TypeOf(b) == reflect.TypeOf(Dog{}) {
        return "ワン"
    } else if reflect.TypeOf(b) == reflect.TypeOf(Cat{}) {
        return "ニャー"
    } else {
        panic("error")
    }
}
func main() {
    dog := Dog{}
    cat := Cat{}
    println(MakeBark(dog))
    println(MakeBark(cat))

}

わざと複雑なコードを書いたわけではありません。でも、うーん......。

以下のような概念が出てきてしまいました。

  • interface{}(PythonでいうところのAny)
  • 型の比較
  • if文

if文!!!!!!!!!!!!!!!!!!!!!!

上のinterfaceを使ったコードには、条件を満たすか確認するコードはありませんでした。つまり、interfaceはを使えばifやcaseといった複雑なコードを取っ払うことができます。

最後に

自分はgolangを触り始めて1ヶ月も経っていません(触り始めたきっかけは業務です)ので、間違いなどあればコメント欄にておっしゃっていただけるとありがたいです。

ところで、弊社はgolangに強い方を探しています。もし自信のある方いましたら、面談しましょう!僕にtwitterのDMとか飛ばして頂いても結構です!(twitterID: @sh1ma)

株式会社エヌエルプラス https://nl-plus.co.jp/

sh1ma
Reverse Engineer / Pythonista
nlplus
インターネットサービスを開発・運営しています。 つよくてニューゲームしたい仲間を募集しています。
https://nl-plus.co.jp/
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