Methods and Interfaces ====================== One of the novel features of Go_ is its use of interfaces. They are designed to provide many of the same benefits of object-oriented programming, but without explicit classes or type inheritance as in C++ or Java_. Methods ------- Before we can discuss interfaces, we need to look at Go_ **methods**. Consider this code:: type Rectangle struct { width, height float64 } // area() is a method func (r Rectangle) area() float64 { return r.width * r.height } // perimeter() is a method func (r Rectangle) perimeter() float64 { return 2 * (r.width + r.height) } // main is a function func main() { r := Rectangle{width:5, height:3} fmt.Printf("dimensions of r: width=%d, height=%d\n", r.width, r.height) fmt.Printf(" area of r: %.2f\n", r.area()) fmt.Printf(" perimeter of r: %.2f\n", r.perimeter()) } Look at the signatures for ``area()`` and ``perimeter()``. The extra parameter in brackets is called the **method receiver**, i.e. ``r`` is the receiver in ``perimeter()``. The receiver is essentially a special input that has some some special privileges. In ``main``, notice how the usual dot-notation is used for calling methods, e.g. ``r.area()`` calls the ``area`` method on ``r``. Method receivers can, if the programmer desires, be passed as pointers to a type. For example, here ``r`` is passed a pointer because ``inflate`` modifies the object ``r`` points to:: func (r *Rectangle) inflate(scale float64) { r.width *= scale r.height *= scale } Without the ``*``, ``r`` would be passed by value, meaning that the code in the body of ``inflate`` would modify this copy instead of the original object that calls the method. Note that there is no change in the syntax for how the fields of ``r`` are accessed: the regular dot-notation is used. Similarly, ``inflate`` is called the same way, e.g. ``r.inflate(2.2)``. There is no special ``->`` operator as in C/C++. An important concept in Go_ is that of **method sets**. `The Go language spec defines method sets like this `_: A type may have a method set associated with it. The method set of an interface type is its interface. The method set of any other type ``T`` consists of all methods declared with receiver type ``T``. The method set of the corresponding pointer type ``*T`` is the set of all methods declared with receiver ``*T`` or ``T`` (that is, it also contains the method set of ``T``). Further rules apply to structs containing embedded fields, as described in the section on struct types. Any other type has an empty method set. In a method set, each method must have a unique non- blank method name. The method set of a type determines the interfaces that the type implements and the methods that can be called using a receiver of that type. So, in our example above, the method set of the type ``Rectangle`` is: {``area``, ``perimeter``}. The method set of ``*Rectangle`` is: {``area``, ``perimeter``, ``inflate``}. Calling a method is defined like this in the `Go spec `_: A method call ``x.m()`` is valid if the method set of (the type of) ``x`` contains ``m`` and the argument list can be assigned to the parameter list of ``m``. If ``x`` is addressable and ``&x``'s method set contains ``m``, ``x.m()`` is shorthand for ``(&x).m()`` .... Interfaces ---------- Here's an example of a Go_ interface:: type Shaper interface { area() float64 perimeter() float64 } The name of this interface is ``Shaper``, and it lists the signatures of the two methods that are necessary to satisfy it. A type ``T`` **implements** ``Shaper`` if ``Shaper``'s method set (the names listed in the interface) is a subset of the method set for ``T``. We saw above that the method set for ``Rectangle`` is {``area``, ``perimeter``}, and the methods listed in ``Shaper`` are clearly a subset of this. Similarly, ``Rectangle*`` also implements ``Shape`` because the methods in ``Shape`` are a subset of the method set of ``Rectangle*``, {``area``, ``perimeter``, ``inflate``}. Importantly, only the *signatures* of the methods are listed in the interface. The bodies of the required functions are not mentioned at all. The implementation is *not* part of the interface. As another example, suppose we add this code:: type Circle struct { radius float64 } func (c Circle) area() float64 { // a method return 3.14 * c.radius * c.radius } func (c Circle) perimeter() float64 { // a method return 2 * 3.14 * c.radius } func (c Circle) diameter() float64 { // a method return 2 * c.radius } The method set of ``Circle`` is {``area``, ``perimeter``, ``diameter``}, and so it implements ``Shaper`` because it contains both ``area`` and ``perimeter``. The method set of ``*Circle`` is also {``area``, ``perimeter``, ``diameter``}, and so ``*Circle`` also implements ``Shaper``. Now we can write code that works on any object of a type that implements ``Shaper``. For example:: func printShape(s Shaper) { fmt.Printf(" area: %.2f\n", s.area()) fmt.Printf(" perimeter: %.2f\n", s.perimeter()) } All that ``printShape`` knows about ``s`` is that the methods in the method set for ``Shaper`` can be called on it. If ``s`` has methods not in the member set for ``Shaper``, then those methods *cannot* be called on ``s``. Here's how you could use ``printShape``:: func main() { r := Rectangle{width:5, height:3} fmt.Println("Rectangle ...") printShape(r) c := Circle{radius:5} fmt.Println("\nCircle ...") printShape(c) } An interesting detail about Go_ interfaces is that you don't need to explicitly tell Go_ that a ``struct`` implements an interface: the compiler figures it out for itself. This contrasts with, for example, Java_, where you must explicitly indicate when a class implements an interface. In summary: - The method set of an interface is all the methods named in the interface. - The method set of a type ``T`` (where ``T`` is not a pointer type) is all methods with a receiver of type ``T``. - The method set of a type ``*T`` (where ``T`` is not a pointer type) is all methods with a receiver of type ``*T`` or ``T``. - A type ``T`` implements an interface ``I`` if the method set of ``I`` is a subset of the method set of ``T``. Example: Using the Sort Interface --------------------------------- Lets see how we can use the standard Go_ `sorting package `_. Suppose we want to sort records of people. In practice, such records might contain lots of information, such as a person's name, address, email address, relatives, etc. But for simplicity we will use this struct:: type Person struct { name string age int } For efficiency, lets sort a slice of pointers to ``Person`` objects; this will avoid moving and copying strings. To help with this, we define the type ``People``:: type People []*Person // slice of pointers to People objects It turns out that it's essential that we create the type ``People``. The code we write below won't compile if we use ``[]*Person`` directly. That's because the method set of ``[]*Person`` does *not* satisfy ``sort.Interface``. So we will add methods on ``People`` that make it implement ``sort.Interface``. For convenience, here's a ``String`` method that prints a ``People`` object:: func (people People) String() (result string) { for _, p := range people { result += fmt.Sprintf("%s, %d\n", p.name, p.age) } return result } The name of this is ``String()``, and it returns a ``string`` object. A method with this signature implements the ``fmt.Stringer`` interface, which allows it to be called by the print functions in ``fmt``. Now we can write code like this:: users := People{ {"Mary", 34}, {"Lou", 3}, {"Greedo", 77}, {"Zippy", 50}, {"Morla", 62}, } fmt.Printf("%v\n", users) // calls the People String method To sort the items in the ``users`` slice, we must create the methods listed in ``sort.Interface``:: type Interface interface { // number of elements in the collection Len() int // returns true iff the element with index i should come // before the element with index j Less(i, j int) bool // swaps the elements with indexes i and j Swap(i, j int) } This interface is pre-defined in the ``sort`` package. Notice that this is a very general interface. It does not even assume that you will be sorting slices or arrays! Three methods are needed:: func (p People) Len() int { return len(p) } func (p People) Less(i, j int) bool { return p[i].age > p[j].age } func (p People) Swap(i, j int) { p[i], p[j] = p[j], p[i] } ``Less`` is the function that controls the order in which the objects will be sorted. By examining ``Less`` you can see that we will be sorting people by age, from oldest to youngest. With these functions written, we can now sort ``users`` like this:: users := People{ {"Mary", 34}, {"Lou", 3}, {"Greedo", 77}, {"Zippy", 50}, {"Morla", 62}, } fmt.Printf("%v\n", users) sort.Sort(users) fmt.Printf("%v\n", users) To change the sort order, modify ``Less``. For instance, this will sort ``users`` alphabetically by name:: func (p People) Less(i, j int) bool { return p[i].name < p[j].name } Another way to sort by different orders is shown in the examples section of the `Go sort package documentation `_. The trick there is to create a new type for every different order you want to sort. Questions --------- #. How would you explain the difference between functions and methods to a programmer who is not familiar with Go_? #. How would you use the ``sort`` package to sort a slice of pointers to ints?