Rick

Rick
Rick

Wednesday, January 16, 2013

Does Go Programming support polymorphism

When you search for Go polymorphism this page is the first thing that shows up. So everyone thinks Go is broken. This means that you should address the issue a little better. If you want to build a community. Since you didn't, I will.
package main
import "fmt";

type Fruit interface {
 Name() string;
 Eat() int;
}

type FruitImpl struct {
 calories int;
}
func (f *FruitImpl) Name() string { return "Fruit"; }
func (f *FruitImpl) Eat() int {
 fmt.Println("Inside Eat(), Name is", f.Name(), "calories=", f.calories);
 c := f.calories;
 f.calories = 0;
 return c;
}

type Apple struct { *FruitImpl; }
func (a *Apple) Name() string { return "Apple"; }

type Banana struct { *FruitImpl; }
func (b *Banana) Name() string { return "Banana"; }

func main() {
 a := &Apple { &FruitImpl{100} };
 b := &Banana { &FruitImpl{200} };
 fmt.Println("Apple Name is", a.Name());
 fmt.Println("Banana Name is", b.Name());
 fruits := [2]Fruit{a,b};
 for i := 0; i < 2; i++ {
  fruits[i].Eat();
 }
}

The above works as expected in Go 1.0. The output is:
Apple Name is Apple
Banana Name is Banana
Inside Eat(), Name is Fruit calories= 100
Inside Eat(), Name is Fruit calories= 200

You can see that Apple name is Apple so the method override worked. You can see that Banana name is Banana so the method override worked. But when you are inside of the method you are not dealing with the interface so you don't get what you expect. You are dealing with FruitImpl. Go does not support type inheritance. You can inherit behavior and data but not type. Thus FruitImpl is calling Name() on FruitImp as you would expect in Go. I think I have a fix for you, although, three years late. Read these articles which explain Go in Java terminology.... http://rick-hightower.blogspot.com/2013/01/java-guide-to-go-programming-article-1.html http://rick-hightower.blogspot.com/2013/01/java-guide-to-go-programming-article-2.html http://rick-hightower.blogspot.com/2013/01/java-guide-to-go-programming-article-3.html http://rick-hightower.blogspot.com/2013/01/java-guide-to-go-programming-article-4.html In short, Go does support polymorphism, data inheritance and behavior inheritance. Polymorphism is only achieved through Go interfaces not structs. You are sharing the Eat() from Fruit. You are sharing get polymorphic behavior for Name(). You are able to do a form of method overriding for Name() albeit through the Fruit interface not through the struct types.
package main

import "fmt"

type Fruit interface {
 Name() string
 Eat() int
}

type FruitImpl struct {
 calories int
 name     string //<--- added this
}

func (f *FruitImpl) Name() string { return f.name; }
func (f *FruitImpl) Eat() int {
 fmt.Println("Inside Eat(), Name is", f.name, "calories=", f.calories) //<--changed this
 c := f.calories
 f.calories = 0
 return c
}

type Apple struct{ *FruitImpl }
func (a *Apple) Name() string { return "Apple" }

type Banana struct{ *FruitImpl }
func (b *Banana) Name() string { return "Banana" }


func main() {
 a := &Apple{&FruitImpl{100, "apple"}}
 b := &Banana{&FruitImpl{200, "banna"}}
 fmt.Println("Apple Name is", a.Name())
 fmt.Println("Banana Name is", b.Name())
 fruits := [2]Fruit{a, b}
 for i := 0; i < 2; i++ {
  fruits[i].Eat()
 }
}

The output is:
Apple Name is Apple
Banana Name is Banana
Inside Eat(), Name is apple calories= 100
Inside Eat(), Name is banna calories= 200
You are sharing the Eat() from Fruit. You are sharing get polymorphic behavior for Name(). If you code to interfaces, then what you want to do is typically a bad design decision anyway. But if you must do something like this, then you can do a type like method override using functions as first class objects.
package main

import "fmt"

type Fruit interface {
 Name() string
 Eat() int
}

type FruitImpl struct {
 calories int
 name func (f *FruitImpl) string 
}

func (f *FruitImpl) Name() string { 
 return f.name(f); 
}

func (f *FruitImpl) Eat() int {
 nameFunc:=f.name
 fmt.Println("Inside Eat(), Name is", nameFunc(f), "calories=", f.calories) //<--changed this
 c := f.calories
 f.calories = 0
 return c
}

type Apple struct{ *FruitImpl }
type Banana struct{ *FruitImpl }

func main() {
 a := &Apple{&FruitImpl{100, func (a *FruitImpl) string { return "Apple" }}}
 b := &Banana{&FruitImpl{200, func (b *FruitImpl) string { return "Banana" }}}
 fmt.Println("Apple Name is", a.Name())
 fmt.Println("Banana Name is", b.Name())
 fruits := [2]Fruit{a, b}
 for i := 0; i < 2; i++ {
  fruits[i].Eat()
 }
}

The internal polymorphic behavior is sort of a crutch and more of an edge case of polymorphism, but as you can see, it can be supported by Go as shown above. The output now matches your desired result.
Apple Name is Apple
Banana Name is Banana
Inside Eat(), Name is Apple calories= 100
Inside Eat(), Name is Banana calories= 200

No comments:

Post a Comment

Kafka and Cassandra support, training for AWS EC2 Cassandra 3.0 Training