Home > Articles > Programming

A Go Primer

  • Print
  • + Share This
This chapter is from the book

Defining Methods

From: methods.go

9  type integer int
10 func (i integer) log() {
11   fmt.Printf("%d\n", i);
12 }
13 func (e *Example) Log() {
14   e.count++
15   fmt.Printf("%d %s\n", e.count, e.Val)
16 }

If you’ve used a class-based language, then you are probably wondering why the last example didn’t define any methods defined on the structure. In Go, you may define methods on any concrete type that you define, not just on structures. The example at the start of this section defines two Log() methods—recall that the uppercase start letter makes them public—one on the structure defined in the last section and one on a named integer type.

The Go type system lets you assign any int to this named type without an explicit cast, but not vice versa. It also prevents you from assigning between two named types. This can be very useful for variables representing quantities. You could, for example, define kilometer and mile types and have the compiler reject any code where you attempted to assign one to the other.

You cannot add methods to existing types—Go does not have an equivalent of Objective-C categories—but you can define new named types and add methods to them.

Methods are declared just like functions, except that there is one extra parameter—the receiver—declared before the function name. One of the interesting syntactic quirks of Go is that there is no this or self keyword. You can give the receiver any name that you want, and this name does not have to be consistent between methods. This idea comes from Oberon-2 and should be popular with people who like the “no magic” philosophy of languages like Objective-C: the receiver is not an implicit hidden parameter that the compiler inserts; it is an explicit parameter just like any other.

The method on the structure in the example at the start of this section takes a pointer as the receiver. This means that it can modify fields of the receiver and these changes will be shared. Methods do not have to take pointers: the other method in the example takes a value. If a method takes a value type, then it can still be called with either a value or a pointer, but it will receive a copy of the structure, so changes that it makes will not be visible from the caller.

When talking about expressions with an explicit type, methods are just functions. You call a method on a structure by using the dot notation, and you declare the parameter that declares how the structure is passed to the method in a special way, but this is just some syntactic sugar. Methods called in this way are semantically equivalent to functions that just take the receiver as an argument: they are statically resolved and are just function calls.

That’s not the real power of methods, though. When you call a method via an interface (described in the next section), you get late-bound dynamic lookup. This dual nature of Go methods means that you have a single abstraction that can be used in the same way as either C types or Smalltalk objects. If you require performance, then you can use statically typed definitions and avoid the dynamic lookup. If you require flexibility, then you can use the late binding mechanism of interfaces.

  • + Share This
  • 🔖 Save To Your Account