1
votes

I'm trying to create some generic functions in go that handle lots of different object types, some of the types embed a handy sub type I've created call BaseObject.

I can't seem to figure out how to test if 'Value interface{}' contains a BaseObject, or how to then call one of it's methods e.g. ToString()... which should return [TestObject] not [BaseObject]

package Test

import(
    "fmt"
    "reflect"
)

func main() {
    Value:=TestObject{}
    TestFunction(Value)
}

//Generic function
func TestFunction(Value interface{}){

    // Does value contain BaseObject? reflect.TypeOf(Value).Containes...Implements??
    //Convert to BaseObject? BO:=Value.(BaseObject)
    // If it does, call BO.ToString()
    //fmt.println(BO.ToString())
}

//Base Object
type BaseObject struct {
}
func (this *HCObject) ToString() string {
    return "[BaseObject]"
}

//Test Object
type TestObject struct{
    BaseObject
}
func (this *TestObject) ToString() string {
    return "[TestObject]"
}
2
See also stackoverflow.com/a/23148998/6309 for more on interface{} - VonC
If you are trying to do "classical" OOP in Go you will get disappointed. Are you sure you cannot find a Go-like solution? - Volker
You'll be better off not trying to write class-style object oriented code in Go. Go doesn't have classes, subclasses, inheritance, and so on. - JimB
These are not subclasses, because there is no inheritance. Please read about embedding and method sets. Embedding is basically automatic delegation. - JimB
Types are embedded in structs, not in interface. You see, this all gets hairy and ugly. There really is a huge impedance mismatch between class based inheritance and struct embedding. Struct embedding is some nice syntactical sugar which reduces typing a few characters. Thats all. - Volker

2 Answers

3
votes

First of all, a couple of points:

  • It is a good practice to give links to working code examples at play.golang.org.
  • Always fmt your code.
  • ToString should be String. See fmt.Stringer interface.
  • As others have pointed out, trying to write Java of C++ in Go will end with a log of pain below the back.

With that said, this is a runnable example of the code that does what you want with many buts.

func TestFunction(v interface{}) {
    fmt.Println(reflect.ValueOf(v).FieldByName("BaseObject").MethodByName("String").Call(nil)[0].String())
}

This code uses the reflect package (which is something you should do only when you really need it). I suggest you play with that example and dig into reflect to see, whether it's worth it to continue the way you go with Go.

2
votes

You don't "embed" into an interface{}. Interfaces themselves have a method set, and contain some value and it's type information.

You extract a value from an interface using a Type Assertion.

Your test function could contain something like:

bo, ok := value.(BaseObject)
if ok {
  fmt.Println(bo)
}

If you want to check for more than one type, you use a type switch. In your case, TestObject and BaseObject are completely different types; TestObject is not a BaseObject.

switch bo := value.(type) {
case TestObject:
    fmt.Println("TestObject", bo)
case BaseObject:
    fmt.Println("BaseObject", bo)
}

If you need to distinguish between the two types, with the embedding type having a superset of the embedded type's methods, define interfaces that match the methods you need.

type Base interface {
    MethodA()
}

type Sub interface {
    MethodA()
    MethodB()
}

In this case Sub is a Base, in that anything that fulfills the Sub interface, also fulfills the Base interface.