reflection detection of subclass fields

720 views
Skip to first unread message

dug...@gmail.com

unread,
Mar 14, 2015, 9:43:21 PM3/14/15
to golan...@googlegroups.com
Hi,
  I have a base type/interface with a func() that, via reflection, will detect its fields.  However, when I subclass it and extend it with new fields that same func() will never detect the new fields. Is this possible with go?  See:  http://play.golang.org/p/yD9tAcl69f
What's interesting, is that if I take that same extended instance and manage to assign it to an interface{} then it works. See:  http://play.golang.org/p/yleqfYKrQk
  Is there any way to do what I'm trying to do?
thanks
-Doug

DV

unread,
Mar 14, 2015, 11:21:22 PM3/14/15
to golan...@googlegroups.com
Embedding is not inheritance. There is no inheritance in Go and no classes. You're trying to make the language do what it was never meant to. It's best to approach whatever problem you're approaching three Go way, not the Java way.

dug...@gmail.com

unread,
Mar 14, 2015, 11:29:58 PM3/14/15
to golan...@googlegroups.com
Does it change anything if we view this problem this way...
I have two variables that are both pointers.  They both point to the same spot in memory. However, 'go' thinks that pointer 1 points to something of type 'Base' while pointer 2 points to something of type 'Extend'.  Is there any way to convert *Base to *Extend ?

Jesse McNelis

unread,
Mar 14, 2015, 11:40:49 PM3/14/15
to Doug Davis, golang-nuts


On 15 Mar 2015 14:29, <dug...@gmail.com> wrote:
>
> Does it change anything if we view this problem this way...
> I have two variables that are both pointers.  They both point to the same spot in memory. However, 'go' thinks that pointer 1 points to something of type 'Base' while pointer 2 points to something of type 'Extend'.  Is there any way to convert *Base to *Extend ?
>

You can't do this in Go. You can't have two pointers to the same object disagree about it's type. Objects only have one type.

You can embed Base in Extend and access the fields of Base. There is no way to get to Extend from a pointer to the embedded Base unless the Base itself contains that pointer.

Go doesn't provide inheritance behaviour but you can fake a lot of it with embedding and manually adding your own parent pointers to your embedded types. But if you're doing this then you're fighting the language and won't have a great time.

dug...@gmail.com

unread,
Mar 14, 2015, 11:49:39 PM3/14/15
to golan...@googlegroups.com, dug...@gmail.com, jes...@jessta.id.au
But isn’t that what I have?  If you look at http://play.golang.org/p/yleqfYKrQk  it seems to me that in the Field() func, both ‘c’ and ‘c.i’ are two pointers pointing to the same thing (due to lines 14 and 15) but ‘c’ thinks its a Base while ‘c.i’ thinks its an Extend.  Or am I not thinking of this correctly?
-Doug

Nigel Tao

unread,
Mar 15, 2015, 12:44:51 AM3/15/15
to dug...@gmail.com, golang-nuts
On Sun, Mar 15, 2015 at 2:29 PM, <dug...@gmail.com> wrote:
> I have two variables that are both pointers. They both point to the same
> spot in memory. However, 'go' thinks that pointer 1 points to something of
> type 'Base' while pointer 2 points to something of type 'Extend'. Is there
> any way to convert *Base to *Extend ?

Not in safe way. There is no representation that the *Base points to a
Base-inside-an-Extend1 and not a Base-inside-an-Extend2.
http://play.golang.org/p/Tvid1ROV1Z

In Java, each object implicitly has per-object overhead to hold the
object's type pointer. With

class Point {
int x;
int y;
}

a Point object has an 8 byte type pointer (for 64-bit pointers) plus 8
bytes for x and y, totalling at least 16 bytes (there's more overhead,
because every Java Object is also a mutex). In Go, with:

type Point struct {
x int32
y int32
}

a Point value has 8 bytes for x and y. That's it.

Go embedding is not inheritance. You can roughly think of

type extend struct {
x int32
base
y int32
}

as 'you can write e.foo instead of e.base.foo' sugar for

type extend struct {
x int32
base base
y int32
}

and it makes about as much sense as asking how to get from the *base
to the *extend as asking how to get from the *int32 for y to the
*extend that surrounds it. No type, whether a struct or an int32,
carries its 'context' as to whether it is part of a larger struct
type.

dug...@gmail.com

unread,
Mar 15, 2015, 1:56:08 AM3/15/15
to golan...@googlegroups.com, dug...@gmail.com
FWIW, I did find that I could do this:
http://play.golang.org/p/p89rjwE5He

Of course, I'm thrilled with having to add the type to initializer on line 19 since its not that much different from what I'm doing on line 15 in http://play.golang.org/p/yleqfYKrQk .  But, its still kind of interesting that I can mess with pointers to fake things out on line 14.

Dan Kortschak

unread,
Mar 15, 2015, 2:11:03 AM3/15/15
to dug...@gmail.com, golan...@googlegroups.com, dug...@gmail.com
It's not clear what the ultimate goal of your code is here, but what is clear is that the path towards that goal will be painful.

If you step back and think about what you are actually trying to acheive with this (and perhaps ask here) then a clearer Go idiom may well (almost certainly will) be available.

Doug Davis

unread,
Mar 15, 2015, 2:31:04 AM3/15/15
to Dan Kortschak, golan...@googlegroups.com
What I’d like to do is have a base interface/struct that has a dynamic getter and setter - e.g.:
    func (b *Base) get( fieldName string )
    func (b *Base) set( fieldName string, val interface{} )
with actual code to walk the fields of “b” to either get/set them - via reflection.

Then I want (and forgive the language) to “subclass” that base entity via something like:
    type MyStruct struct {
        Base
        Name string
    }
such that I can then do:
    var me &MyStruct{}

And then I can do:
    me.Name = “John”
but I can also do:
    me.set( “Name”, “John” )

I can get almost all of it to work.  The part where it fails is when the get/set functions are called.  In there when I use reflection to walk the resource on which the function was invoked I only see “Base” fields and not “MyStruct” fields despite the fact that get/set are actually being invoked on “me” (a MyStruct).  Or at least that’s how it feels.

So, the options I’ve come across are:
1 - add an interface{} field (called “self”) to ‘Base’ and somehow get &me into it as a *MyStruct and not a *Base.  Then in get/set when I walk the resource’s fields I don’t walk ‘b’ but instead I walk “self”.  That works because “self”, while defined as an interface{}, is actually a *MyStruct.  The annoying part about this is that the code that does the &MyStruct  would also need to do some kind of me.self = me   - works, but looks kind of goofy.

2 - similar to #1, I add a field to “base” that holds the type of MyStruct.  Then in get/set I can convert “b” to a pointer to MyStruct.  Like I showed in my last note.  This still requires the code that does the &MyStruct to do something special.  

Hope some of that makes sense.  :-)

Jesse McNelis

unread,
Mar 15, 2015, 2:57:33 AM3/15/15
to Doug Davis, Dan Kortschak, golan...@googlegroups.com
On Sun, Mar 15, 2015 at 5:30 PM, Doug Davis <dug...@gmail.com> wrote:
> I can get almost all of it to work. The part where it fails is when the
> get/set functions are called. In there when I use reflection to walk the
> resource on which the function was invoked I only see “Base” fields and not
> “MyStruct” fields despite the fact that get/set are actually being invoked
> on “me” (a MyStruct). Or at least that’s how it feels.

Types that embed another type forward method calls to that type.
While the method is being called on MyStruct, it's being forwarded to
the method on Base.
The method on Base doesn't know about MyStruct and thus can't access
it's fields.

The problem you're trying to solve is best solved with a function not a method.
Something like: func Set(a interface{}, key string, value interface{}){}
This function would use the reflect package to find the field 'key' on
the given value 'a' and assign it the value 'value'.

This package implements such a function,
http://godoc.org/github.com/oleiade/reflections#SetField

Dan Kortschak

unread,
Mar 15, 2015, 3:03:11 AM3/15/15
to Doug Davis, golan...@googlegroups.com
That's still really an implementation approach rather than a goal. Why do you want this? What is the ultimate goal?

Doug Davis

unread,
Mar 15, 2015, 9:47:33 AM3/15/15
to Dan Kortschak, golan...@googlegroups.com
I want to have a single entity that encapsulates all of these to make it easier for people to deal with. While what Jesse said, about using a global function Set(obj, name, value), is do-able for some actions but its not ideal.  It works for Set/Get (although not as nice looking) but I also want to have additional data in the Base struct - like a filename so that it can also support Load() and Save().  Then any place in my code someone can just say me.Save() because whoever setup “me” originally already defined the  filename.  A global Save() might look something like this:
but then I run into the issue where go doesn’t let me pass in a *MyStruct, it wants a *Base - not sure if its possible to convert it, but even if there is, that’s kind of annoying that all callers would have to do that.  Advice on this one would be much appreciated.  :-)

So, this is really all about the UX of the user of MyStruct. I’m sure there are ways to get the net result that I want, but I’m trying to make the user of MyStruct’s life as easy as possible, which would normally mean calls like:
me = &MyStruct{ … }
me.Name = “John”
me.Set( “Name”, “John” )
me.Save()

Ian Lance Taylor

unread,
Mar 15, 2015, 1:25:16 PM3/15/15
to Doug Davis, Dan Kortschak, golan...@googlegroups.com
On Sun, Mar 15, 2015 at 6:47 AM, Doug Davis <dug...@gmail.com> wrote:
>
> I want to have a single entity that encapsulates all of these to make it
> easier for people to deal with.

In Go, think of that single entity as the package, not the type.

Go is not a language that encourages you to attach all information
about a type to that type. For example, there is no Go equivalent to
a C++ public static class variable.

Ian

Jesse McNelis

unread,
Mar 15, 2015, 7:02:45 PM3/15/15
to Doug Davis, golang-nuts, kortschak

On 16 Mar 2015 00:47, "Doug Davis" <dug...@gmail.com> wrote:
>
> So, this is really all about the UX of the user of MyStruct. I’m sure there are ways to get the net result that I want, but I’m trying to make the user of MyStruct’s life as easy as possible, which would normally mean calls like:
> me = &MyStruct{ … }
> me.Name = “John”
> me.Set( “Name”, “John” )
> me.Save()
>

Code reuse in Go is done by having types implement interfaces and having reusable functions that take those interfaces as parameters.

Code reuse in Java or C++ is more commonly done through inheritance, Go code isn't written like that.

Instead of having your types know how to save() themselves, instead you have a save() function that knows how to save any type that implements an interface.

It's just as easy to call some database.Save(someValue) as it is to call someValue.save().

The database.Save() version would be able to save any type that implemented the interface it expected, even if that type didn't know about your Base type. Could be implemented by non struct types that don't have fields or be a wrapper for another interface type. Eg. A wrapper for an io.Reader that reads json from a stream and makes it available for saving.

The Go style leads to more code duplication in places where inheritance would reduce duplication but less duplication in other areas so it balances out.

Eg. Should a save() method return an error? You'd need to decide this in your Base type. But some saving might not be able to result in errors, like saving to a in memory database. So database.Save(someValue) might need error handling, but memoryDatabase.save(someValue) wouldn't.

Reply all
Reply to author
Forward
0 new messages