Spearfisher
Spearfisher

Reputation: 8773

Understanding interfaces in Go

I am trying to understand how interfaces work in Go.

Let's say I have 2 structs:

package "Shape"

type Square struct {
   edgesCount int
}

type Triangle struct {
   edgesCount int
}

Now I create a Shape interface:

type Shape interface {

}

Why can't I specify that the Shape interface has an egdesCount property? Are interfaces only supposed to regroup methods?

Another problem I face is sharing function. Isn't possible to come up with something like this:

func New() *Shape {
  s:=new(Shape)
  s.edgesCount = 0
  return s
}

This would be much better than having to rewrite the exact same code:

func New() *Square {
  s:=new(Square)
  s.edgesCount = 0
  return s
}

func New() *Triangle {
  s:=new(Triangle)
  s.edgesCount = 0
  return s
}

(which also poses problem as I cannot redeclare my New function...)

Many thanks for your help

Upvotes: 0

Views: 233

Answers (3)

iccananea
iccananea

Reputation: 391

Interface only group methods, as their purpose is to define behavior, very much like in other languages (see Java Interfaces, for example).

What you are looking for is something like code inheritance, which doesn't exist in Go. BUT, you can get something similar with struct embedding:

Go does not provide the typical, type-driven notion of subclassing, but it does have the ability to “borrow” pieces of an implementation by embedding types within a struct or interface.

So, you can get what you want by doing the following (play link):

type Shape struct {
    edgeCount int
}

func (s Shape) EdgeCount() int {
    return s.edgeCount
}

type Square struct {
    Shape
}

type Triangle struct {
    Shape
}

func main() {
    sq := Square{Shape{edgeCount: 4}}
    tr := Square{Shape{edgeCount: 3}}
    fmt.Println(sq.EdgeCount())
    fmt.Println(tr.EdgeCount())
}

Upvotes: 0

VonC
VonC

Reputation: 1323115

What you are referring to isn't interface (which allows to pass an object as that interface, simply because the object is a receiver for all the interface method).
Here, an empty interface{}' Shape would be satisfied by any type, which isn't useful here.

It is more about type embedding (using an anonymous type structure for instance):

That would promote the common field edgesCount to both struct.
As the spec mentions:

A field or method f of an anonymous field in a struct x is called promoted if x.f is a legal selector that denotes that field or method f.

See this example:

type Shape struct {
    edgesCount int
}

type Square struct {
    Shape
}

type Triangle struct {
    Shape
}

func NewSquare() *Square {
    return &Square{
        Shape{edgesCount: 4},
    }
}
func NewTriangle() *Triangle {
    return &Triangle{
        Shape{edgesCount: 3},
    }
}

func main() {
    fmt.Printf("Square %+v\n", NewSquare())
    fmt.Printf("Triangle %+v\n", NewTriangle())
}

Output:

Square &{Shape:{edgesCount:4}}
Triangle &{Shape:{edgesCount:3}}

Upvotes: 3

tgrosinger
tgrosinger

Reputation: 2579

Go isn't an object oriented language, and those fields are internal fields since they start with lower case letters. Instead, try something like this:

type Shape interface {
    EdgeCount() int
}

type Square struct {
   edgesCount int
}

func (s Square) EdgeCount() int {
    return s.edgesCount
}

type Triangle struct {
   edgesCount int
}

func (t Triangle) EdgeCount() int {
    return t.edgesCount
}

Now you can do things using the EdgeCount function on either type of object since they both implement the Shape interface.

func IsItSquare(s Shape) bool {
    // If it has 4 sides, maybe
    return s.EdgeCount == 4
}

But you will still need to create the different types of shapes with independent New functions, or just by declaring them literally.

// Obviously a contrived example
s := Triangle{edgesCount: 3}

Upvotes: 2

Related Questions