blob: ba25d80e1dd765b029d6b12e0785c6a6d3e4c770 [file] [log] [blame]
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 struct types.
The _method_receiver_ appears in its own argument list between the `func` keyword and the method name.
.play methods/methods.go
* Methods continued
You can declare a method on _any_ type that is declared in your package, not just struct types.
However, you cannot define a method on a type from another package (including built in types).
.play methods/methods-continued.go
* Methods with pointer receivers
Methods can be associated with a named type or a pointer to a named type.
We just saw two `Abs` methods. One on the `*Vertex` pointer type and the other on the `MyFloat` value type.
There are two reasons to use a pointer receiver. First, to avoid copying the value on each method call (more efficient if the value type is a large struct). Second, so that the method can modify the value that its receiver points to.
Try changing the declarations of the `Abs` and `Scale` methods to use `Vertex` as the receiver, instead of `*Vertex`.
The `Scale` method has no effect when `v` is a `Vertex`. `Scale` mutates `v`. When `v` is a value (non-pointer) type, the method sees a copy of the `Vertex` and cannot mutate the original value.
`Abs` works either way. It only reads `v`. It doesn't matter whether it is reading the original value (through a pointer) or a copy of that value.
.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/strings][http://localhost:4000/string]] and
[[http://localhost:4000/strings][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]].