is it possible to call overridden method from parent struct in Golang?

54,346

Solution 1

As you wrote, what Go has is not really inheritance, the method that allows inheritance like features is called Embedding.

http://golang.org/doc/effective_go.html#embedding

What it means basically is that the embedded struct has no idea that it is embedded, so you cannot override anything that is called from it. You can actually take the embedded struct and take a reference for it only from the embedding struct.

So your best way to do it is more or less like your second example - through some sort of dependency injection using interfaces. i.e - A has a reference to some interface that does the actual work, say worker, that writes to a file or whatever. Then when instantiating B, you also replace A's worker with another worker (you can do it even without embedding A of course). The A just does something like myWorker.Work() without caring what worker it is.

Solution 2

Been struggling with this myself. Found 2 solutions:

  1. Idiomatic Go way: implement the common "method" as external function with interface as argument.

     package main
    
     import "fmt"
    
     // Fooer has to Foo
     type Fooer interface {
         Foo()
     }
    
     // Bar is a proxy, that calls Foo of specific instance.
     func Bar(a Fooer) {
         a.Foo()
     }
    
     //////////////////////////////////////////////////////////////////////
     // usage
    
     func main() {
         b := &B{} // note it is a pointer
         // also there's no need to specify values for default-initialized fields.
         Bar(b) // prints: B.Foo()
     }
    
     //////////////////////////////////////////////////////////////////////
     // implementation
    
     // A is a "base class"        
     type A struct {
     }
    
     func (a *A) Foo() {
         fmt.Println("A.Foo()")
     }
    
     // B overrides methods of A
     type B struct {
         A
     }
    
     func (b *B) Foo() {
         fmt.Println("B.Foo()")
     }
    

Try it on Go Playground: https://play.golang.org/p/2TbmHUs9_Dt

  1. Similar to your second option: interface hackery. However, since Bar() is not specific to A (it is common to A and B), let's move it to base class, and hide implementation details and all dangerous stuff:

     package main
    
     import "fmt"
    
     //////////////////////////////////////////////////////////////////////
     // Usage
    
     func main() {
         b := NewB()
         b.Bar() // prints: B.Foo()
    
         a := NewA()
         a.Bar() // prints: A.Foo()
     }
    
     //////////////////////////////////////////////////////////////////////
     // Implementation.
    
     // aBase is common "ancestor" for A and B.
     type aBase struct {
         ABCD // embed the interface. As it is just a pointer, it has to be initialized!
     }
    
     // Bar is common to A and B.
     func (a *aBase) Bar() {
         a.Foo() // aBase has no method Foo defined, so it calls Foo method of embedded interface.
     }
    
     // a class, not exported
     type a struct {
         aBase
     }
    
     func (a *a) Foo() {
         fmt.Println("A.Foo()")
     }
    
     // b class, not exported
     type b struct {
         aBase
     }
    
     func (b *b) Foo() {
         fmt.Println("B.Foo()")
     }
    
     //////////////////////////////////////////////////////////////////////
     // Now, public functions and methods.
    
     // ABCD describes all exported methods of A and B.
     type ABCD interface {
         Foo()
         Bar()
     }
    
     // NewA returns new struct a
     func NewA() ABCD {
         a := &a{}
         a.ABCD = a
         return a
     }
    
     // NewB returns new struct b
     func NewB() ABCD {
         b := &b{}
         b.ABCD = b
         return b
     }
    

Try it on Go Playground: https://play.golang.org/p/0Zcs_arturP

Solution 3

Recently I have a need to do this and the composition method proposed by OP works great.

I try to create another example to try to demonstrate the parent and child relationship and make it easier to read.

https://play.golang.org/p/9EmWhpyjHf:

package main

import (
    "fmt"
    "log"
)

type FruitType interface {
    Wash() FruitType
    Eat() string
}

type Fruit struct {
    name  string
    dirty bool
    fruit FruitType
}

func (f *Fruit) Wash() FruitType {
    f.dirty = false
    if f.fruit != nil {
        return f.fruit
    }
    return f
}
func (f *Fruit) Eat() string {
    if f.dirty {
        return fmt.Sprintf("The %s is dirty, wash it first!", f.name)
    }
    return fmt.Sprintf("%s is so delicious!", f.name)
}

type Orange struct {
    *Fruit
}

func NewOrange() *Orange {
    ft := &Orange{&Fruit{"Orange", true, nil}}
    ft.fruit = ft
    return ft
}
func NewApple() *Fruit {
    ft := &Fruit{"apple", true, nil}
    return ft
}

func (o *Orange) Eat() string {
    return "The orange is so sour!"
}

func main() {
    log.Println(NewApple().Eat())
    log.Println(NewApple().Wash().Eat())
    log.Println(NewOrange().Eat())
    log.Println(NewOrange().Wash().Eat())
}

Solution 4

Go does not support virtual method overriding. The design pattern you want to use is thus not directly supported by Go. It is considered bad practice because changing the implementation of A.Bar() would break all derived classes like B that assume A.Foo() will be called by A.Bar(). The design pattern you want to use will make your code brittle.

The way to do it in Go would be to define a Fooer interface with a Foo() method. A Fooer would be passed as argument to Bar() or stored in a field of A and called by A.Bar(). To change the Foo action, change the Fooer value. This is called composition, and it is much better than changing Foo action by inheritance and method overriding.

Here is an idiomatic way to do what you want to do in Go: https://play.golang.org/p/jJqXqmNUEHn. In this implementation, the Fooer is a member field of A that is initialized by a parameter of to the instance factory function NewA(). This design pattern is preferable when the Fooer doesn't change frequently during the lifetime of A. Otherwise, you may pass the Fooer as parameter of the Bar() method.

This is how we change the behavior of Foo() in Go. It is called composition because you change the behavior of Bar() by changing the instances composing A.

package main

import (
    "fmt"
)

type Fooer interface {
    Foo()
}

type A struct {
    f Fooer
}

func (a *A) Bar() {
    a.f.Foo()
}

func NewA(f Fooer) *A {
    return &A{f: f}
}

type B struct {
}

func (b *B) Foo() {
    fmt.Println("B.Foo()")
}

type C struct {
}

func (c *C) Foo() {
    fmt.Println("C.Foo()")
}

func main() {
    a := NewA(new(B))
    a.Bar()

    a.f = &C{}
    a.Bar()
}

PS: Here is a possible implementation of the design pattern you wanted to implement for documentation purpose: https://play.golang.org/p/HugjIbYbout

Solution 5

package main

import (
    "fmt"
)


//-- polymorphism in work

// children specification by methods signatures
// you should define overridable methods here
type AChildInterface interface {
    Foo()
}

type A struct {
    child AChildInterface
}

//-- /polymorphism in work


// hard A.Bar method
func (a *A) Bar() {
    a.child.Foo() // Foo() will be overwritten = implemented in a specified child
}


//-- default implementations of changeable methods

type ADefaults struct{}

func (ad ADefaults) Foo() {
    fmt.Println("A.Foo()")
}

//-- /default


//-- specified child

type B struct {
    ADefaults // implement default A methods from ADefaults, not necessary in this example
}

// overwrite specified method
func (b B) Foo() {
    fmt.Println("B.Foo()")
}

//-- /specified child

func main() {
    a := A{ADefaults{}}
    a.Bar()

    // Golang-style inheritance = embedding child
    b := A{B{}} // note: we created __Parent__ with specified __Child__ to change behavior
    b.Bar()
}

Output:

A.Foo()
B.Foo()
Share:
54,346
zhaozhi
Author by

zhaozhi

I used to be an PHP engineer in Tencent and Youku, and now I'm working for Baidu. I'm interested in mysql, PHP, PYTHON, and now golang

Updated on July 05, 2022

Comments

  • zhaozhi
    zhaozhi almost 2 years

    I want to implement such code, where B inherit from A and only override A's Foo() method, and I hope the code to print B.Foo(), but it still print A.Foo(), it seems that the receiver in Golang can't work like this in C++, in which when dynamic binding is enabled, the code can work like what I want.

    I also post another piece of code, which works, but it's too hard to implement, and more like a hack way, I think it's not a Golang style.

    So my problem is: if the parent's Bar() method has some logic, for example, open a file, then read some lines, and use Foo() to output these lines to stdout, and the Child (in the example is B) wants to use most of them, the only difference is that the Child wants Foo() to output the lines to another file. How should I implement it? I have heard that Golang's inheritance can't work like C++ or Java, and what's right way in Golang?

    package main 
    
    import ( 
            "fmt" 
    ) 
    
    type A struct { 
    } 
    
    func (a *A) Foo() { 
            fmt.Println("A.Foo()") 
    } 
    
    func (a *A) Bar() { 
            a.Foo() 
    } 
    
    type B struct { 
            A 
    } 
    
    func (b *B) Foo() { 
            fmt.Println("B.Foo()") 
    } 
    
    func main() { 
            b := B{A: A{}} 
            b.Bar() 
    }
    
    output: A.Foo()
    

    the following piece works, but when write

    a := A{}
    a.Bar()
    

    you will encounter a compiler error

    package main
    
    import (
        "fmt"
    )
    
    type I interface {
        Foo()
    }
    
    type A struct {
        i I
    
    }
    
    func (a *A) Foo() {
        fmt.Println("A.Foo()")
    }
    
    func (a *A) Bar() {
        a.i.Foo()
    
    }
    
    type B struct {
        A
    }
    
    func (b *B) Foo() {
        fmt.Println("B.Foo()")
    }
    
    func main() {
        b := B{A: A{}}
        b.i = &b     // here i works like an attribute of b
        b.Bar()
    
    output: B.Foo()
    
  • zhaozhi
    zhaozhi over 10 years
    in fact, the second example uses composition to achieve, both A and B composite an interface I, this is what golang encounrages.
  • Not_a_Golfer
    Not_a_Golfer over 10 years
    yeah, as I wrote your second example is the way to go. I'd just change it so the parent struct is not its own interface.
  • Dac0d3r
    Dac0d3r almost 9 years
    Not_a_Golfer, what do you mean by "parent struct is not its own interface" - how would it look in code, what you're suggesting? :-)
  • neurosnap
    neurosnap almost 9 years
    I'm interested as well, this answer solved a problem I was having overriding a method in an embedded struct, curious about what the proper code looks like.
  • Not_a_Golfer
    Not_a_Golfer almost 9 years
    @neurosnap hell, I wrote this almost 2 years ago, I have no idea what I meant :)
  • neurosnap
    neurosnap almost 9 years
    Hah. My problem is those interfaced methods use struct properties so I need to embed the interface. Is that considered bad practice? My issue is how do I have an interface separate from the structure without a lot of redundant function params?
  • tim.tadh
    tim.tadh about 8 years
    I tried to explain the differences and have a section on how you can kind of achieve method overriding here: hackthology.com/object-oriented-inheritance-in-go.html
  • Lucas Cimon
    Lucas Cimon about 5 years
    Very interesting, thanks, but I don't see any actual call of the child method on a variable with the parent type
  • antichris
    antichris almost 4 years
    If this is the state of a "decent" C++ style code, no wonder why people are jumping ship to languages like Go and Rust. play.golang.org/p/klY36h890k3
  • GalloCedrone
    GalloCedrone over 3 years
    Really easier to follow than everything else +1