| Methods and interfaces |
| This lesson covers methods and interfaces, the constructs that define objects and their behavior. |
| |
| The Go Authors |
| https://golang.org |
| |
| * Methods |
| |
| Go does not have classes. |
| However, you can define methods on types. |
| |
| A method is a function with a special _receiver_ argument. |
| |
| The receiver appears in its own argument list between the `func` keyword and |
| the method name. |
| |
| In this example, the `Abs` method has a receiver of type `Vertex` named `v`. |
| |
| .play methods/methods.go |
| |
| * Methods are functions |
| |
| Remember: a method is just a function with a receiver argument. |
| |
| Here's `Abs` written as a regular function with no change in functionality. |
| |
| .play methods/methods-funcs.go |
| |
| * Methods continued |
| |
| You can declare a method on non-struct types, too. |
| |
| In this example we see a numeric type `MyFloat` with an `Abs` method. |
| |
| You can only declare a method with a receiver whose type is defined in the same |
| package as the method. |
| You cannot declare a method with a receiver whose type is defined in another |
| package (which includes the built-in types such as `int`). |
| |
| .play methods/methods-continued.go |
| |
| * Pointer receivers |
| |
| You can declare methods with pointer receivers. |
| |
| This means the receiver type has the literal syntax `*T` for some type `T`. |
| (Also, `T` cannot itself be a pointer such as `*int`.) |
| |
| For example, the `Scale` method here is defined on `*Vertex`. |
| |
| Methods with pointer receivers can modify the value to which the receiver |
| points (as `Scale` does here). |
| Since methods often need to modify their receiver, pointer receivers are more |
| common than value receivers. |
| |
| Try removing the `*` from the declaration of the `Scale` function on line 16 |
| and observe how the program's behavior changes. |
| |
| With a value receiver, the `Scale` method operates on a copy of the original |
| `Vertex` value. |
| (This is the same behavior as for any other function argument.) |
| The `Scale` method must have a pointer receiver to change the `Vertex` value |
| declared in the `main` function. |
| |
| .play methods/methods-pointers.go |
| |
| * Pointers and functions |
| |
| Here we see the `Abs` and `Scale` methods rewritten as functions. |
| |
| Again, try removing the `*` from line 16. |
| Can you see why the behavior changes? |
| What else did you need to change for the example to compile? |
| |
| (If you're not sure, continue to the next page.) |
| |
| .play methods/methods-pointers-explained.go |
| |
| * Methods and pointer indirection |
| |
| Comparing the previous two programs, you might notice that |
| functions with a pointer argument must take a pointer: |
| |
| var v Vertex |
| ScaleFunc(v) // Compile error! |
| ScaleFunc(&v) // OK |
| |
| while methods with pointer receivers take either a value or a pointer as the |
| receiver when they are called: |
| |
| var v Vertex |
| v.Scale(5) // OK |
| p := &v |
| p.Scale(10) // OK |
| |
| For the statement `v.Scale(5)`, even though `v` is a value and not a pointer, |
| the method with the pointer receiver is called automatically. |
| That is, as a convenience, Go interprets the statement `v.Scale(5)` as |
| `(&v).Scale(5)` since the `Scale` method has a pointer receiver. |
| |
| .play methods/indirection.go |
| |
| * Methods and pointer indirection (2) |
| |
| The equivalent thing happens in the reverse direction. |
| |
| Functions that take a value argument must take a value of that specific type: |
| |
| var v Vertex |
| fmt.Println(AbsFunc(v)) // OK |
| fmt.Println(AbsFunc(&v)) // Compile error! |
| |
| while methods with value receivers take either a value or a pointer as the |
| receiver when they are called: |
| |
| var v Vertex |
| fmt.Println(v.Abs()) // OK |
| p := &v |
| fmt.Println(p.Abs()) // OK |
| |
| In this case, the method call `p.Abs()` is interpreted as `(*p).Abs()`. |
| |
| .play methods/indirection-values.go |
| |
| * Choosing a value or pointer receiver |
| |
| There are two reasons to use a pointer receiver. |
| |
| The first is so that the method can modify the value that its receiver points to. |
| |
| The second is to avoid copying the value on each method call. |
| This can be more efficient if the receiver is a large struct, for example. |
| |
| In this example, both `Scale` and `Abs` are with receiver type `*Vertex`, |
| even though the `Abs` method needn't modify its receiver. |
| |
| In general, all methods on a given type to should have either value or pointer |
| receivers, but not a mixture of both. |
| (We'll see why over the next few pages.) |
| |
| .play methods/methods-with-pointer-receivers.go |
| |
| * Interfaces |
| |
| An interface type is defined by a set of methods. |
| |
| A value of interface type can hold any value that implements those methods. |
| |
| *Note:* There is an error in the example code on line 22. |
| `Vertex` (the value type) doesn't satisfy `Abser` because |
| the `Abs` method is defined only on `*Vertex` (the pointer type). |
| |
| .play methods/interfaces.go |
| |
| * Interfaces are satisfied implicitly |
| |
| A type implements an interface by implementing the methods. |
| There is no explicit declaration of intent; no "implements" keyword. |
| |
| Implicit interfaces decouple implementation packages from the packages that define the interfaces: neither depends on the other. |
| |
| It also encourages the definition of precise interfaces, because you don't have to find every implementation and tag it with the new interface name. |
| |
| [[https://golang.org/pkg/io/][Package io]] defines `Reader` and `Writer`; you don't have to. |
| |
| .play methods/interfaces-are-satisfied-implicitly.go |
| |
| * Stringers |
| |
| One of the most ubiquitous interfaces is [[//golang.org/pkg/fmt/#Stringer][`Stringer`]] defined by the [[//golang.org/pkg/fmt/][`fmt`]] package. |
| |
| type Stringer interface { |
| String() string |
| } |
| |
| A `Stringer` is a type that can describe itself as a string. The `fmt` package |
| (and many others) look for this interface to print values. |
| |
| .play methods/stringer.go |
| |
| * Exercise: Stringers |
| |
| Make the `IPAddr` type implement `fmt.Stringer` to print the address as |
| a dotted quad. |
| |
| For instance, `IPAddr{1,`2,`3,`4}` should print as `"1.2.3.4"`. |
| |
| .play methods/exercise-stringer.go |
| |
| * Errors |
| |
| Go programs express error state with `error` values. |
| |
| The `error` type is a built-in interface similar to `fmt.Stringer`: |
| |
| type error interface { |
| Error() string |
| } |
| |
| (As with `fmt.Stringer`, the `fmt` package looks for the `error` interface when |
| printing values.) |
| |
| Functions often return an `error` value, and calling code should handle errors |
| by testing whether the error equals `nil`. |
| |
| i, err := strconv.Atoi("42") |
| if err != nil { |
| fmt.Printf("couldn't convert number: %v\n", err) |
| return |
| } |
| fmt.Println("Converted integer:", i) |
| |
| A nil `error` denotes success; a non-nil `error` denotes failure. |
| |
| .play methods/errors.go |
| |
| * Exercise: Errors |
| |
| Copy your `Sqrt` function from the [[/flowcontrol/8][earlier exercise]] and modify it to return an `error` value. |
| |
| `Sqrt` should return a non-nil error value when given a negative number, as it doesn't support complex numbers. |
| |
| Create a new type |
| |
| type ErrNegativeSqrt float64 |
| |
| and make it an `error` by giving it a |
| |
| func (e ErrNegativeSqrt) Error() string |
| |
| method such that `ErrNegativeSqrt(-2).Error()` returns `"cannot`Sqrt`negative`number:`-2"`. |
| |
| *Note:* a call to `fmt.Sprint(e)` inside the `Error` method will send the program into an infinite loop. You can avoid this by converting `e` first: `fmt.Sprint(float64(e))`. Why? |
| |
| Change your `Sqrt` function to return an `ErrNegativeSqrt` value when given a negative number. |
| |
| .play methods/exercise-errors.go |
| |
| * Readers |
| |
| The `io` package specifies the `io.Reader` interface, |
| which represents the read end of a stream of data. |
| |
| The Go standard library contains [[https://golang.org/search?q=Read#Global][many implementations]] of these interfaces, including files, network connections, compressors, ciphers, and others. |
| |
| The `io.Reader` interface has a `Read` method: |
| |
| func (T) Read(b []byte) (n int, err error) |
| |
| `Read` populates the given byte slice with data and returns the number of bytes |
| populated and an error value. It returns an `io.EOF` error when the stream |
| ends. |
| |
| The example code creates a |
| [[//golang.org/pkg/strings/#Reader][`strings.Reader`]]. |
| and consumes its output 8 bytes at a time. |
| |
| .play methods/reader.go |
| |
| * Exercise: Readers |
| |
| Implement a `Reader` type that emits an infinite stream of the ASCII character |
| `'A'`. |
| |
| .play methods/exercise-reader.go |
| |
| * Exercise: rot13Reader |
| |
| A common pattern is an [[https://golang.org/pkg/io/#Reader][io.Reader]] that wraps another `io.Reader`, modifying the stream in some way. |
| |
| For example, the [[https://golang.org/pkg/compress/gzip/#NewReader][gzip.NewReader]] function takes an `io.Reader` (a stream of compressed data) and returns a `*gzip.Reader` that also implements `io.Reader` (a stream of the decompressed data). |
| |
| Implement a `rot13Reader` that implements `io.Reader` and reads from an `io.Reader`, modifying the stream by applying the [[https://en.wikipedia.org/wiki/ROT13][rot13]] substitution cipher to all alphabetical characters. |
| |
| The `rot13Reader` type is provided for you. |
| Make it an `io.Reader` by implementing its `Read` method. |
| |
| .play methods/exercise-rot-reader.go |
| |
| * Web servers |
| |
| [[https://golang.org/pkg/net/http/][Package http]] serves HTTP requests using any value that implements `http.Handler`: |
| |
| package http |
| |
| type Handler interface { |
| ServeHTTP(w ResponseWriter, r *Request) |
| } |
| |
| In this example, the type `Hello` implements `http.Handler`. |
| |
| Visit [[http://localhost:4000/][http://localhost:4000/]] to see the greeting. |
| |
| #appengine: *Note:* This example won't run through the web-based tour user |
| #appengine: interface. To try writing web servers you may want to |
| #appengine: [[https://golang.org/dl/][Install Go]]. |
| |
| .play methods/web-servers.go |
| |
| * Exercise: HTTP Handlers |
| |
| Implement the following types and define ServeHTTP methods on them. Register them to handle specific paths in your web server. |
| |
| type String string |
| |
| type Struct struct { |
| Greeting string |
| Punct string |
| Who string |
| } |
| |
| For example, you should be able to register handlers using: |
| |
| http.Handle("/string", String("I'm a frayed knot.")) |
| http.Handle("/struct", &Struct{"Hello", ":", "Gophers!"}) |
| |
| After starting your server, you will be able to visit |
| [[http://localhost:4000/string][http://localhost:4000/string]] and |
| [[http://localhost:4000/struct][http://localhost:4000/struct]]. |
| |
| For more details about the `http.Handle` function, see |
| [[https://golang.org/pkg/net/http/#Handle][the documentation]]. |
| |
| #appengine: *Note:* This example won't run through the web-based tour user |
| #appengine: interface. To try writing web servers you may want to |
| #appengine: [[https://golang.org/dl/][Install Go]]. |
| |
| .play methods/exercise-http-handlers.go |
| |
| * Images |
| |
| [[https://golang.org/pkg/image/#Image][Package image]] defines the `Image` interface: |
| |
| package image |
| |
| type Image interface { |
| ColorModel() color.Model |
| Bounds() Rectangle |
| At(x, y int) color.Color |
| } |
| |
| *Note*: the `Rectangle` return value of the `Bounds` method is actually an |
| [[https://golang.org/pkg/image/#Rectangle][`image.Rectangle`]], as the |
| declaration is inside package `image`. |
| |
| (See [[https://golang.org/pkg/image/#Image][the documentation]] for all the details.) |
| |
| The `color.Color` and `color.Model` types are also interfaces, but we'll ignore that by using the predefined implementations `color.RGBA` and `color.RGBAModel`. These interfaces and types are specified by the [[https://golang.org/pkg/image/color/][image/color package]] |
| |
| .play methods/images.go |
| |
| * Exercise: Images |
| |
| Remember the picture generator you wrote earlier? Let's write another one, but this time it will return an implementation of `image.Image` instead of a slice of data. |
| |
| Define your own `Image` type, implement [[https://golang.org/pkg/image/#Image][the necessary methods]], and call `pic.ShowImage`. |
| |
| `Bounds` should return a `image.Rectangle`, like `image.Rect(0,`0,`w,`h)`. |
| |
| `ColorModel` should return `color.RGBAModel`. |
| |
| `At` should return a color; the value `v` in the last picture generator corresponds to `color.RGBA{v,`v,`255,`255}` in this one. |
| |
| .play methods/exercise-images.go |
| |
| * Congratulations! |
| |
| You finished this lesson! |
| |
| You can go back to the list of [[/list][modules]] to find what to learn next, or continue with the [[javascript:click('.next-page')][next lesson]]. |