0
0

More than 3 years have passed since last update.

embeded使い方【golang】

Last updated at Posted at 2020-03-01

embeded

  • golangで継承のようなことをしたいときに使うembeded
  • イマイチ理解できなかったのでまとめました。
  • Javaなどのオブジェクト指向言語の継承と同じ使い方をするとスーパークラスとして初期化する際のエラーオーバーライドできないなどの問題が発生する
  • ※キャプチャと本記事のコードとで初期化時の値が異なることがありますが本質的なところは変わらないのでご了承ください。

embededの例

  • まずは構造体に何らかのメソッドを持たせる
  • Employees(従業員)とBoss(上司)にしてみる
main.go
type Employees struct {
    name string
}

func newEmployees(n string) *Employees {
    e := new(Employees)
    e.name = n
    return e
}

func (e *Employees) Hello() string {
    return "名前:" + e.name  // >>
}
  • これに部長という意味でBossという構造体を加えてみる
main.go
type Boss struct {
  Employees  // embeded
  age string  // Staff独自のフィールドを持たせるとする
}

func newBoss(n, a string) *Boss{
  b := new(Boss)
  b.name = n
  b.age = a

  return b
}

  • ↑の書き方で、BossEmployeesの持ち物(フィールド、メソッド)を貰う(委譲)することができる
  • これまでのソースをまとめて、試しに以下のようにEmployeesBossを初期化してそれぞれのインスタンスでメソッドHelloを呼んでみる
main.go
package main

import "fmt"

type Employees struct {
    name string
}

func newEmployees(n string) *Employees {
    e := new(Employees)
    e.name = n
    return e
}

func (e *Employees) Hello() string {
    return "名前:" + e.name  // >>
}

type Boss struct {
  Employees  // embeded
  age string  // Boss独自のフィールドを持たせるとする
}

func newBoss(n, a string) *Boss{
  b := new(Boss)
  b.name = n
  b.age = a

  return b
}

func main() {
    employee := newEmployees("従業員A")
    boss := newBoss("部長A ", "11")
    fmt.Println(employee.Hello())
    fmt.Println(boss.Hello())
}

1.png

  • この時点では継承のそれと同じ動作のように見える
  • 次にオーバーライドができるか。以下のように年齢も出力するHelloメソッドを追加
  • Bossは年齢も持っているため年齢も出してみる
main.go
func (b *Boss) Hello() string {
    return b.name + b.age
}

2.png

  • それぞれのHelloメソッドの内容が出力されているのでこれも想定通りのオーバーライド

  • ではではJavaで言うところのスーパークラス型の変数への代入はどうか

  • Employees型の変数にBossのインスタンスを入れるイメージで書いてみる

main.go
var e *Employees
e = newBoss("従業員B","11")


// Javaで書くと
// Boss b = new Boss("従業員B","11");
// Employees e = b;

3.png

ダメでしたー

  • golangでは「子型 is a 親型」は使えないみたい

Bossでの構造体の初期化

main.go
boss := &Boss{name: "従業員C", age: "10"}
  • これだとエラーになります

4.png

  • 正しくは以下のようにするそうです
main.go
boss := &Boss{
    Employees: Employees{name: "従業員C"}, 
    age: "10"
}
  • このことから何がわかるかと言うと、BossはEmployees型を持っているということ

オーバーライドの仕方

  • 今までの検証でわかるようにBossはEmployeesを継承しているわけではなくBossがEmployeesを持っているということだったのがわかりました。なので、継承してメソッドをオーバーライドするぞーとしてもオーバーライドしてくれません。

  • 例:Helloを使った別のメソッドを作成して呼んでみる

main.go
func (e *Employees) SignUp() string {
    return e.Hello() + "で登録"
}
  • 全体
main.go
package main

import "fmt"

type Employees struct {
    name string
}

func newEmployees(n string) *Employees {
    e := new(Employees)
    e.name = n
    return e
}

func (e *Employees) Hello() string {
    return "名前:" + e.name
}

type Boss struct {
  Employees  // embeded
  age string  // Boss独自のフィールドを持たせるとする
}

func newBoss(n, a string) *Boss{
  b := new(Boss)
  b.name = n
  b.age = a

  return b
}

func (b *Boss) Hello() string {
    return b.name + b.age
}

func (e *Employees) SignUp() string {
    return e.Hello() + "で登録"
}

func main() {
    employee := newEmployees("従業員A")
    boss := newBoss("部長A ", "11")
    fmt.Println(employee.Hello())
    fmt.Println(boss.Hello())
}

5.png

  • オーバーライドしたはずなのにBossのHelloに書いた年齢が出力されない。。。
  • これは結局のところ、両方のHelloメソッドともにEmployeesのものということです。

ポリモーフィズムを実現するには??

  • ポリモーフィズム:引数を受け取ったインスタンスがクラスによって違う振る舞いをすること
  • golangでポリモーフィズムを実現するにはembededinterfaceを使用するらしい

インタフェースを定義する

main.go
type Person interface {
    Hello() string
}
  • PersonにHelloメソッドを持っているインスタンスを格納していくイメージ
  • SignUpはこのPersonを引数に受け取るようにする
main.go
func SingUp(p Person) string {
    return p.Hello()  + "で登録"
}
  • で、mian.goで呼び出し
main.go
func main() {
    ps := []Person{
        newEmployees("従業員A")
        newBoss("部長A ", "11"),
    }
    for _, p := range ps {
        fmt.Println(SignUp(p))
    }
}

全体はこう

main.go
package main

import "fmt"

type Person interface {
    Hello() string
}

type Employees struct {
    name string
}

func newEmployees(n string) *Employees {
    e := new(Employees)
    e.name = n
    return e
}

func (e *Employees) Hello() string {
    return "名前:" + e.name
}

type Boss struct {
  Employees  // embeded
  age string  // Boss独自のフィールドを持たせるとする
}

func newBoss(n, a string) *Boss{
  b := new(Boss)
  b.name = n
  b.age = a

  return b
}

func (b *Boss) Hello() string {
    return b.name + b.age
}

func SignUp(p Person) string {
    return p.Hello()  + "で登録"
}

func main() {
    ps := []Person{
        newEmployees("従業員A"),
        newBoss("部長A ", "11"),
    }
    for _, p := range ps {
        fmt.Println(SignUp(p))
    }
}

6.png

  • ちょっと不細工ではありますが、無事それぞれのHelloメソッド(年齢を出す)を出力できました。

参考(むしろこっち読んだ方がわかりやすいです)

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