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
}
- ↑の書き方で、
Boss
はEmployees
の持ち物(フィールド、メソッド)を**貰う(委譲)**することができる - これまでのソースをまとめて、試しに以下のように
Employees
とBoss
を初期化してそれぞれのインスタンスでメソッド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())
}
- この時点では
継承
のそれと同じ動作のように見える - 次にオーバーライドができるか。以下のように年齢も出力するHelloメソッドを追加
- Bossは年齢も持っているため年齢も出してみる
main.go
func (b *Boss) Hello() string {
return b.name + b.age
}
-
それぞれの
Hello
メソッドの内容が出力されているのでこれも想定通りのオーバーライド -
ではではJavaで言うところのスーパークラス型の変数への代入はどうか
-
Employees型の変数にBossのインスタンスを入れるイメージで書いてみる
main.go
var e *Employees
e = newBoss("従業員B","11")
// Javaで書くと
// Boss b = new Boss("従業員B","11");
// Employees e = b;
ダメでしたー
- golangでは
「子型 is a 親型」
は使えないみたい
Bossでの構造体の初期化
main.go
boss := &Boss{name: "従業員C", age: "10"}
- これだとエラーになります
- 正しくは以下のようにするそうです
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())
}
- オーバーライドしたはずなのにBossのHelloに書いた年齢が出力されない。。。
- これは結局のところ、両方のHelloメソッドともにEmployeesのものということです。
ポリモーフィズムを実現するには??
-
ポリモーフィズム
:引数を受け取ったインスタンスがクラスによって違う振る舞いをすること - golangでポリモーフィズムを実現するには
embeded
とinterface
を使用するらしい
インタフェースを定義する
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))
}
}
- ちょっと不細工ではありますが、無事それぞれのHelloメソッド(年齢を出す)を出力できました。
参考(むしろこっち読んだ方がわかりやすいです)
- Go言語で「embedded で継承ができる」と思わないほうがいいのはなぜか?:めちゃめちゃ参考にしました。