It seems like I missed something important but I can not figure out what it is. I use reflect to access embedded type fields through an interface. The problem I have is that according to runtime/pprof
it eats up a lot of CPU. I do not like to implement Setter and Getter methods on all Vehicles so is there a better way of doing this?
Simplified sample:
package main
import(
"reflect"
"fmt"
)
// the "contract" is that all vehicles have an embedded Engine
type Vehicle interface {}
type Engine struct {
Power float64
Cubic float64
}
type Car struct {
Engine
Weight float64
TopSpeed float64
}
// more Vehicles with Engines here...
func EngineCheck(v Vehicle) {
// this does not work:
//power := v.Power
// reflection works but eats up a lot of CPU:
power := reflect.ValueOf(v).Elem().FieldByName("Power").Interface().(float64)
fmt.Println(power)
}
func main() {
c1 := &Car{Engine{120.0, 1.2}, 1.5, 250}
EngineCheck(c1)
}
You could use type assertion if you know the exact type which is fast, and only revert to reflection if that fails.
For example:
func EngineCheck(v Vehicle) {
var power float64
if eng, ok := v.(*Car); ok {
power = eng.Power
} else {
power = reflect.ValueOf(v).Elem().FieldByName("Power").Interface().(float64)
}
fmt.Println(power)
}
Note that the types Car
and *Car
are different, and the above example would only "skip" the reflection part if the value you pass is indeed a pointer: *Car
.
If there are multiple possible "acceptable" types, you could use a type switch. For example if you pass a Car
or *Car
, you can get the Power
value from both. Also if Engine
or *Engine
would be passed, the same thing applies.
func EngineCheck(v Vehicle) {
var power float64
switch i := v.(type) {
case *Car:
power = i.Power
case Car:
power = i.Power
case *Engine:
power = i.Power
case Engine:
power = i.Power
default:
power = reflect.ValueOf(v).Elem().FieldByName("Power").Interface().(float64)
}
fmt.Println(power)
}
But the idiomatic solution would still be to add a getter function to Vehicle
:
type Vehicle interface {
GetPower() float64
}
Note that you do not have to implement GetPower()
everywhere. If you implement it at the Engine
:
func (e Engine) GetPower() float64 {
return e.Power
}
And you embed Engine
into Car
(as you did), your Car
type will automatically have this GetPower()
method (promoted) in its method set and thus it will automatically implement Vehicle
. And then your EngineCheck()
function would be as simple as:
func EngineCheck(v Vehicle) {
fmt.Println(v.GetPower())
}
Try all these 3 variants on the Go Playground.