A Programmer's Guide To Go Part 2 - Objects and Interfaces
Written by Mike James   
Thursday, 25 July 2019
Article Index
A Programmer's Guide To Go Part 2 - Objects and Interfaces
Wot No Inheritance?
Interfaces

In the second part of our mini-series on Google's alternative to C, it is time to come face to face with Go's most interesting features - objects and interfaces

A Programmers Guide To Languages

languagecover

Contents

  1. Octave 
  2. Scratch 2 *revised
  3. Node.js *revised
  4. App Inventor 2 *revised
  5. Getting Started With .NET IL
  6. NetLogo
  7. Small Basic  *revised
  8. TypeScript *revised
  9. Google Apps Script
  10. Python
  11. Google's Go
    1. A Programmer's Guide To Go With Visual Studio Code
    2. A Programmer's Guide To Go Part 2 - Objects And Interfaces
    3. A Programmer's Guide To Go Part 3 - Goroutines And Concurrency
  12. Guide to F#
  13. Ruby: Object-Oriented Approach
               A Functional Language

 

Part 2  Objects And Interfaces

goicon2

Go takes a very different approach to objects than the class based object-oriented languages that most programmers are familiar with. You don't define a class and then use it to instantiate an object and there is no type hierarchy. There also is no inheritance, no function overloading and... 

If this all sound as if Go is going to turn out to be a primitive low level language think again. Its approach is best described as low level yes but it also quite sophisticated. 

Objects

So how do you create objects in Go?

The simple answer is you don't exactly create objects. You simply associate functions with types and you can use these functions as if they were methods of the objects or values that you create using the types.

The way that this is implemented is very simple and very much like the way that languages such as JavaScript do the job.  All you have to do  is to assign a "receiver" to a function to create a method.

A receiver acts like an extra parameter of a specified type passed to the function. If you know how objects work in other languages then you might recognize it as being very similar to the "this" parameter passed into object methods. 

For example,  

type point struct {
        x, y int    
}

func (p point) Sum() int {
        return p.x+p.y
}

this binds the function Sum to the receiver type point. You can see that it looks like an additional parameter for the function and indeed this is what it is. The function you have created corresponds to a function type with a signature which has an additional parameter of the receiver type as its first parameter. This is also how receivers are implemented - it is very simple and low level. Notice also that unlike the use of a default receiver e.g. "this" you can give your receiver a sensible name.  

Now when you create a point value you can also call the Sum method as if it was a method belonging to a point object. For example:

var p1 = point{1, 5}
fmt.Println(p1.Sum())

When you use the Sum method the value that it is being called from is passed as the receiver parameter.  This is how one function definition can be shared between a lot of different values of the same type. 

If you are puzzled by this then it is worth pointing out that the whole of object oriented programming starts from a simple syntactic transformation. If you have a function which operates on some data you would generally write something like:

myfunction(data,other parameters)

That is you would pass the data as a parameter. However if you allow this to be written as:

data.myfunction(other parameters)

you instantly have a different perspective. Now myfunction somehow belongs to "data" - it is a method rather than a function. Go approaches object orientation by making this simple syntactic change the basis for how it defines a method.

Notice that while in this example the method was attached to a struct you can attach methods to any type - except for the built in types. If you want to attach a method to a built in type you have to first create a new type with the same underlying type. 

For example if you want to create an integer "object" with a square method you would use something like:

type myint int
func (i myint) square() myint {
    return i * i
}

After this you can do things like:

var j myint = 3    
fmt.Println(j.square())

There is just one complication but it isn't a difficult one.  Go passes all parameters by value and this includes the receiver. This is fine as long as you only want to make use of the values that the receiver provides. If you want to change the receiver in any way you need to pass a pointer.

For example, if we change myint's square method to read:

func (i myint) square() myint {
    i = i * i
    return i
}

Then it returns the same result but it doesn't change the value stored in i. To do this you have to pass the receiver as a pointer to the type:

func (i *myint) square() myint {
    *i = *i * *i
    return *i
}

Now when you do:

var j myint = 3    
fmt.Println(j.square())

the result 9 is displayed but also j is changed to 9. Notice that you don't have to change the way you use a method to take advantage of passing a pointer Go takes care of it for you. However you do have to write your method function remembering to dereference the pointer. 

It is true that the statement:

*i=*i * *i 

looks horrible, but this is about as bad an example as I can find. In practice things look better than this and the type checking helps you to not  forget a dereference operator when you need one..

That's about all there is to Go objects. As stated in the introduction there is no inheritance and methods are not overloaded. 

goicon1

<ASIN:1469769166>

<ASIN:0321774639>



Last Updated ( Saturday, 03 August 2019 )