Tommy
Tommy

Reputation: 13632

golang: question about interface implementation inheritance - Stack Overflow

This question is about the cleanest way to "inherit" interface implementations in golang. I understand Go does not have inheritance; this question is about how people achieve it otherwise, hence the quotes around inheritance.

Let's say theres a standard library interface defined, for example container/heap: https://golang.org/pkg/container/heap/#Interface

Lets say there's a .go file called pq.go that implements that interface:

//pq.go
import ("container/heap")
type PriorityQueue []*Vertex

func (pq PriorityQueue) Len() int { return len(pq) }

func (pq PriorityQueue) Less(i, j int) bool {
...
}

func (pq PriorityQueue) Swap(i, j int) {
...
}

func (pq *PriorityQueue) Push(x interface{}) {
...
}

func (pq *PriorityQueue) Pop() interface{} {
...
}

Now let's say I just want a Tiny variant on this, called MaxPQ, in maxpq.go, for where I want to override a single implementation detail of pq.go, e.g., override Less(..). How do I achieve this without literally copying the last file over, changing the type name, and changing the implementation of the single function, e.g., less?

Meaning, is there a way to make a new implementation of an interface that is very similar to another?

Doing this, literally copying it over, seems drastic and changes would need to be made in multiple places:

//maxpq.go
import ("container/heap")
type MaxPriorityQueue []*Vertex

func (pq MaxPriorityQueue) Len() int { return len(pq) }

func (pq MaxPriorityQueue) Less(i, j int) bool {
...
}

func (pq MaxPriorityQueue) Swap(i, j int) {
...
}

func (pq *MaxPriorityQueue) Push(x interface{}) {
...
}

func (pq *MaxPriorityQueue) Pop() interface{} {
...
}

Upvotes: 0

Views: 417

Answers (2)

Pharaoh
Pharaoh

Reputation: 752

You can try something like this using composition.

type iTemp interface {
    func1()
    func2()
}

type base struct {
}

func (base) func1() {
    fmt.Println("base func1")
}
func (base) func2() {
    fmt.Println("base func2")
}


type child struct {
    base
}

func (child) func1() {
    fmt.Println("child func1")
}

func main(){
    printMessage(base{})
    printMessage(child{})
}

func printMessage(t iTemp) {
    t.func1()
    t.func2()
}

output:

base func1  
base func2  
child func1  
base func2  

Here in child struct you are providing a new implementation of func1 function of base class, but you still have access to base class implementation, you can still call it using child{}.base.func1()

Upvotes: 0

Burak Serdar
Burak Serdar

Reputation: 51512

There are multiple ways you can do this.

You can define a new type based on the original type, and delegate all methods:

type OriginalType struct {...}

func (o OriginalType) F() {...}

type NewType OriginalType

func (n NewType) F() { OriginalType(n).F() }

With this, you need to redefine all methods of the original type, as the NewType does not "inherit" the methods of the OriginalType.

You can embed:

type NewType struct {
   OldType
}

Now you have a NewType.F() function, but it will operate on the OldType portion of the NewType. If you want to redeclare that, you can:

func (n NewType) F() { 
   n.OldType.F(); 
   // Do more stuff
}

This will work like inheritance if you pass either instance through an interface. That is:

type IntF interface {
   F()
}

func f(v IntF) {
  v.F()
}

If you pass a NewType, then NewType.F will be called.

However, you have to pass an interface, you cannot pass the embedded struct to get the behavior of the enclosing object:

func f(v OriginalType) {
   v.F()
}

func main() {
   n:=NewType()
   // f(n) Won't work
   f(n.OriginalType)

Above, only the OriginalType part of n is sent to the function f, and OriginalType.F will be called.

Upvotes: 1

Related Questions