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]"
}
Gordon Truslove
  • 724
  • 2
  • 10
  • 19
  • See also http://stackoverflow.com/a/23148998/6309 for more on `interface{}` – VonC Sep 02 '14 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? – Volker Sep 02 '14 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. – JimB Sep 02 '14 at 14:29
  • I'm converting a very large project. So I'm trying to recreate as much in Go as possible, to make the conversion easier. I just keep stumbling on things like this. Go supports sub classes, so I don't really see why I shouldn't use them. – Gordon Truslove Sep 02 '14 at 14:32
  • 3
    These are *not* subclasses, because there is no inheritance. Please read about [embedding](http://golang.org/doc/effective_go.html#embedding) and [method sets](http://golang.org/ref/spec#Method_sets). Embedding is basically automatic delegation. – JimB Sep 02 '14 at 14:34
  • Fair enough. So what I'm looking is to see if a type is embedded in interface{}. subclass is the wrong word. – Gordon Truslove Sep 02 '14 at 14:40
  • 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. – Volker Sep 02 '14 at 14:44

2 Answers2

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.

Ainar-G
  • 34,563
  • 13
  • 93
  • 119
  • 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. – Gordon Truslove Sep 02 '14 at 15:38
  • @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](http://play.golang.org/p/VyoUW18NGU). Note that you do have to ensure that you pass a pointer to match the method set. – JimB Sep 02 '14 at 16:13
  • 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. – Gordon Truslove Sep 02 '14 at 20:30
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.

JimB
  • 104,193
  • 13
  • 262
  • 255
  • I was trying both of these before but neither work. Only value.(TestObject) and case TestObject: works. – Gordon Truslove Sep 02 '14 at 14:55
  • 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. – JimB Sep 02 '14 at 15:09