A Programmer's Guide To Go Part 2 - Objects And Interfaces
Written by Mike James   
Thursday, 05 December 2013
Article Index
A Programmer's Guide To Go Part 2 - Objects And Interfaces
Interfaces

Anonymous Fields

If you create an anonymous field then the struct gets all of the fields of the struct you are aggregating.  For example, if you change the definition of ellipse to:

type ellipse struct {
    circle
    rh float32
    rv float32
}

the circle is now "embedded" in the ellipse stuct - its fields are embedded in the ellipse. As a result you can now reference the fields of the circle as if they belonged to the ellipse: 

fmt.Println(myellipse.center.x)

 

This all seems very reasonable and embedding is a good idea. What might surprise you is that that when you embed a struct in a struct you also embed its methods. That is, ellipse has an area method that can be called using:

myellipse.area()

What is surprising here is that area has a receiver of type circle but now it is working with a receiver of type ellipse. In fact the ellipse type has an area method that will accept either a circle or an ellipse as its receiver. This is an example of a promoted method, i.e. one that is defined on one struct but used by another as if it had been defined for it. 

Of course, the promoted area method computes the area of a circle not an ellipse. At this point you are probably thinking it's time to override the area method promoted from circle and this is exactly what you can do. If you define an ellipse area method:

func (e ellipse) area() float32 {
    return 3.14 * e.rh * e.rv
}

then the circle's area method isn't promoted. 

Notice that the function has to have the same name and signature as the method that would be promoted in its place. 

You can see that the embedding mechanism gives you many of the features of inheritance. What it lacks is the usual hierarchical structure of classes and sub-classes but this is often regarded as being too difficult to manage in most projects. That is, complex inheritance hierarchies are bad.

Go doesn't have hierarchies, you simply add objects together. Notice that you can have multiple anonymous fields and this means that Go's embedding is more like multiple than single inheritance.

goicon2

Interfaces

There is still a small problem that we have to solve but it isn't obvious at first. 

As Go is statically typed you can't make a mistake using non-existent methods. For example, if you try:

mypoint:=point{10,10}
mypoint.area()

then you will generate a compile time error. The compiler can check to see if point has an area method and when it finds it doesn't it flags the error. 

Now consider the problem of writing a function that can work with any "shape" object that has an area method - e.g. circle and ellipse from the previous section. What you want to write is something like:

func display(s) {
    r := s.area()
    fmt.Print(r)

} 

This doesn't work because function parameters have to be typed. One solution is to assign a specific type:

func display(s circle) {
    r := s.area()
    fmt.Print(r)
}


This works, but not for objects of type ellipse even though they have an area function. 

To avoid having to define a function for each type Go has interface types. An interface type is a collection of methods or a "method set". For example we could define a measurable interface as the method set that consists of just one member - i.e. the area function. 

type measurable interface {
    area() float32
}

Of course an interface type can have lots of methods defined not just one. 

If you know about interfaces as implemented in other languages you might be expect that each of object that supports the interface would have to declare this fact. That is, to be regarded as an example of an interface type an object could have to declare that it implements the interface. This is not how Go works. 

In Go the set of methods that an object supports can be worked out by the compiler, so why bother the programmer with the need to declare that an object implements an interface - it either does or it doesn't. So, for example, after you have declared the measurable interface you can write a function that makes use of it as its parameter type - eg.

func display(s measurable) {
    r := s.area()
    fmt.Print(r)
}

Now you can call display with any object that has the draw method in its method set. That is both:

    display(mycircle)
and

    display(myellipse)

work without any modifications to circle or ellipse. 

That is, an object satisfies an interface if it contains the method set of the interface within its method set. 

Notice also that as almost any type can have methods the range of possible things that can satisfy an interface is wider than you might expect. 

And that's all there is to the interface type and how you use it. 

 goicon1

 

A Programmer's Guide To Go

  1. A Programmer's Guide To Go With LiteIDE
  2. A Programmer's Guide To Go Part 2 - Objects And Interfaces
  3. A Programmer's Guide To Go Part 3 - Goroutines And Concurrency

More Information:

http://golang.org

Related articles:

A Programmer's Guide To Go With LiteIDE

Go 1.2 Is Released

Go Is Four

Go Programming Language Turns 3       

Getting started with Google's Go

Why invent a new language? Go creator explains

Ready to Go - Go Reaches Version 1

Go in Google App Engine

Google App Engine Go-es Forward

Go with Google - Yet Another Language!

A Programmer's Guide to Scratch 2

Getting Started With NetLogo

Getting Started With TypeScript

A Programmer's Guide To Octave

Getting Started With Google App Script

 

Banner

 

To be informed about new articles on I Programmer, install the I Programmer Toolbar, subscribe to the RSS feed, follow us on, Twitter, FacebookGoogle+ or Linkedin,  or sign up for our weekly newsletter.

 

blog comments powered by Disqus

<ASIN:0321817141>

<ASIN:1478355824>



Last Updated ( Thursday, 09 January 2014 )
 
 

   
RSS feed of all content
I Programmer - full contents
Copyright © 2014 i-programmer.info. All Rights Reserved.
Joomla! is Free Software released under the GNU/GPL License.