1

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]"
}
7
  • See also stackoverflow.com/a/23148998/6309 for more on interface{} Commented Sep 2, 2014 at 14:26
  • 2
    If you are trying to do "classical" OOP in Go you will get disappointed. Are you sure you cannot find a Go-like solution? Commented Sep 2, 2014 at 14:29
  • 3
    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. Commented Sep 2, 2014 at 14:29
  • 3
    These are not subclasses, because there is no inheritance. Please read about embedding and method sets. Embedding is basically automatic delegation. Commented Sep 2, 2014 at 14:34
  • 2
    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. Commented Sep 2, 2014 at 14:44

2 Answers 2

3

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.

Sign up to request clarification or add additional context in comments.

3 Comments

This looks like the solution. I'm still trying to get it to work in my own code but you're demo on play.golang.org works. Thank you.
@GordonTruslove: There's no need to use reflection in this case (you should avoid it when possible) -- you can do this completely with interfaces EXAMPLE. Note that you do have to ensure that you pass a pointer to match the method set.
That example is the best solution. I'm going to try to implement that. It just means a bit of extra work to implement all the extra interface methods. thanks.
2

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.

2 Comments

I was trying both of these before but neither work. Only value.(TestObject) and case TestObject: works.
Try creating run-able examples. The error from the type assertion spells out your problem panic: interface conversion: interface is main.TestObject, not main.BaseObject. Again, there is no inheritance in Go. There is no type->embedded-type is-a relationship. Embedding is only a convenient form of delegation, and you use interfaces to achieve polymorphism.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.