.. highlight:: go types2.go ========= :: // types2.go package main import ( "fmt" "math" "strings" ) /////////////////////////////////////////////////////////////////////////////////// // // Point // /////////////////////////////////////////////////////////////////////////////////// type Point struct { x, y float64 } // This is like a constructor: it creates a new object of type Point.q func makeOrigin() Point { return Point{0.0, 0.0} } // This is a method: p is called the method's receiver. It's like the "this" // pointer in C++ or Java. // // Any method with the exact name String() string will then be called by print // functions in fmt. func (p Point) String() string { return fmt.Sprintf("(%v, %v)", p.x, p.y) } // This is an ordinary function: it is not a method. // It's called like dist(p, q). func dist(a, b Point) float64 { dx := a.x - b.x dy := a.y - b.y return math.Sqrt(dx*dx + dy*dy) } // This is a method version of dist. // It's called like p.distTo(q). func (p Point) distTo(other Point) float64 { return dist(p, other) } /////////////////////////////////////////////////////////////////////////////////// // // Color // /////////////////////////////////////////////////////////////////////////////////// // 0 <= uint8 <= 255 type Color struct { r, g, b uint8 } // This is a method: c is the receiver. func (c Color) String() string { return fmt.Sprintf("RGB(%v, %v, %v)", c.r, c.g, c.b) } // A (global) map of names and their colors. var colorName map[string]Color = map[string]Color{ "red": Color{255, 0, 0}, "green": Color{0, 255, 0}, "blue": Color{0, 0, 255}, } // Returns the color of the given name. // If name is not a known color, black and false is returned. // If name is known, the associated color and true is returned. func makeColor(name string) (Color, bool) { n := strings.TrimSpace(name) n = strings.ToLower(n) c, ok := colorName[n] if ok { return c, true } else { return Color{0, 0, 0}, false } } // makeColor // Creates and returns a new grayscale color. func makeGray(n uint8) Color { return Color{n, n, n} } // This is a method, with receiver c of type Color. func (c Color) brightness() float64 { return float64(c.r+c.g+c.b) / 255.0 } // The type of c is *Color (instead of just Color) because invert() modifies // c. If there was no * and c were just of type Color, then c would be passed // by a value, and invert() would modify that copy --- which is useless. // // Also note that we even though c is of type *Color (i.e. pointer to Color), // we still use "." to access the values in it. In C/C++, you would use -> in // this case. func (c *Color) invert() { c.r, c.g, c.b = 255-c.r, 255-c.g, 255-c.b } // // Types in Go have an associated **method set**. // // For the type Color, the associated method set is all the methods that have // a Color as a receiver, i.e. brightness and String in this example. // // For the type *Color, the associated method set is all the methods that have // a *Color as a receiver, or Color as a receiver, i.e. brightness, String, and // invert in this example. // // In general, the method set of a type T are all the methods have T as a // receiver. The method set of a type *T are all the methods that have either // *T or T as a receiver. // // Method sets are important when we get to interfaces: a type implements an // interface if its method set is a superset of the methods listed in the // interface. // /////////////////////////////////////////////////////////////////////////////////// // // Colored point // /////////////////////////////////////////////////////////////////////////////////// // Go does *not* have type inheritance as found in object-oriented languages // like C++, Java, or Python. Instead, it allows you to **embed** a struct // within another struct. // // For a good discussion of embedding, see this section of the Effective Go: // https://golang.org/doc/effective_go.html#embedding type ColoredPoint struct { Color Point } /////////////////////////////////////////////////////////////////////////////////// // // test code // /////////////////////////////////////////////////////////////////////////////////// func pointTest() { p := Point{2, -3} fmt.Println(p) fmt.Println(dist(Point{0, 0}, Point{1, 1})) } func colorTest() { c, _ := makeColor(" Red ") fmt.Println(c) c.invert() fmt.Println(c) g := makeGray(144) fmt.Println(g) g.invert() fmt.Println(g) } func coloredPointTest() { red, _ := makeColor("red") cp := ColoredPoint{red, Point{2, 3}} fmt.Println(cp) cp.x = 4 fmt.Println(cp) // Note that invert is in the method set for *Color, and not ColoredPoint // or *ColoredPoint. By embedding Color in ColoredPoint, Go lets us call // its methods through cp. cp.invert() fmt.Println(cp) fmt.Println(cp.distTo(makeOrigin())) // this next line causes a compiler error since cp is of type ColorPoint, // but dist expects a two values of type Point // fmt.Println(dist(cp, makeOrigin())) } func main() { pointTest() colorTest() coloredPointTest() } // main