I have a some structs,
type Fruit struct {
Name string
Sweetness int
}
type Meat struct {
Name string
Bloodiness int
}
Somtimes a person may eat some fruit, sometimes some meat. So we have another struct.
type Person struct {
Name string
Mealtype interface{}
}
It's this 'Mealtype' interface{}
bit I kind of made up to fix my issue.
Go is allowing me to set either a Mealtype to be a Meat or Fruit struct, however. I can't seem to access any of the internal data from the struct.
The fmt.Println( someperson.Mealtype )
doesn't offer my to access either .Bloodiness
or .Sweetness
For example, if i do:
f := Fruit{}
f.Name = "Orange"
f.Sweetness = 10
p := Person{}
p.Name = "John"
p.Mealtype = f
fmt.Println(p.Mealtype.Name)
I get the error:
p.Mealtype.Name undefined (type interface{} has no field or method Name)
CodePudding user response:
If you want both Fruit
and Meat
to share a Name
value that can be accessed, you probably want to create an interface that the two of them implement. For example
type Food interface {
Name() string
}
func (f Fruit) Name() string {
return f.name
}
func (m Meat) Name() string {
return m.name
}
type Person struct {
Name string
Mealtype Food
}
(note that the Meat and Fruit's field name
is now lowercase, to avoid conflict with Name
)
Then you should be able to call fmt.Println(p.Mealtype.Name())
.
For completeness, you can also use type assertion, but that's probably not what you want to do in your example. But it would look something like this:
if fruit, ok := p.MealType.(Fruit); ok {
fmt.Println(fruit.Name)
}
CodePudding user response:
You should describe Mealtype interface before using it. Like
type MealtypeInterface interface {
Name() string
}
Then in type Person you define Mealtype as MealtypeInterface, not interface{}
type Person struct {
Name string
Mealtype MealtypeInterface
}
CodePudding user response:
an interface defines a consistent way of interacting with variables of inconsistent types. So you have to find a way to make accessing Bloodiness
of meats and Sweetness
of fruits consistently.
Here's one way that Sweetness
and Bloodiness
could be behind a consistent interface:
interface Edible {
PrimaryAttribute() (string, int)
}
func (f Fruit)PrimaryAttribute() (string, int) {
return "Sweetness", f.Sweetness
}
// and return Bloodiness, m.Bloodiness for meats of course
Its common for programmers used to interpreted languages like python or javascript, etc, to think of struct field names as runtime-accessible data. But this is only the case in Go with reflect
, which I try to discourage new Go programmers to use. Ask yourself if you wouldn't rather define a consistent method of describing arbitrary named attributes of your edibles:
type Edible struct {
FlavorAttributes map[string]int
}
fish_meat := &Edible{FlavorAttributes: map[string]int{"Bloodiness": 3}
Now Bloodiness
and Sweetness
are not fields, they are strings in a map, and more accessible at runtime.
It's common for programming books to talk about polymorphism in terms of human hierarchical organization (class Animal, class Dog, class Labrador hierarchy). Read "Design Patterns: Elements of Reusable Object-Oriented Software" for some good thoughts about why a lot of software doesn't end up implementing class hierarchies from the human perspective.