Object Oriented Programming
CIS 193 – Go Programming
Prakhar Bhandari, Adel Qalieh
CIS 193
Prakhar Bhandari, Adel Qalieh
CIS 193
Remember to run gofmt
!
Test your code!
Helpful Tools
- go
vet
- golint
Aggregate data type that groups zero or more named values of arbitrary types
type Person struct { ID int Name string Age int } var jim Person jim.Age = 34
Field order matters
Fields are only exported if uppercase first letter
type Point struct { X, Y int }
Values in order - every field must be included
p1 := Point{1, 2}
Field names and values
p2 := Point{X: 2} // Y = 0
Cannot use both forms in the same literal
If each field is comparable, structs can be compared using the == operator
Instead of
type Circle struct { X, Y, Radius int }
We can do
type Point struct { X, Y int } type Circle struct { Center Point Radius int }
Why would we want to do this?
Accessing fields
var c Circle c.Center.X = 3 c.Center.Y = 4 w.Radius = 6
This is messy - what would be a better way?
type Point struct { X, Y int } type Circle struct { Point Radius int }
Accessing fields
var c Circle c.X = 3 c.Y = 4 w.Radius = 6
Point is embedded in Circle
How do we initialize structs with anonymous embedded fields?
c := Circle{X: 4, Y: 5, Radius: 8} // compile error
We have two options
c := Circle{Point{8, 8}, 5} c := Circle{ Point: Point{X: 8, Y: 8}, Radius: 5, // this comma is necessary }
Struct type T can't have field T
How can we make recursive types?
A variable is a piece of storage that contains a value
Pointers are the address of a variable, the location where a value is stored
Pointers allow us to read/update the value of a variable indirectly
var x int &x // the "address of x", has type *int
&x
is a pointer to an int
func incr(p *int) int { *p++ // doesn't change p return *p } x := 1 incr(&x) // x is now 2 fmt.Println(incr(&x)) // "3" (and x is 3)
*p
is the variable to which p
points to
type Person struct { ID int Name string Age int } var p Person name := &p.Name *name = "Bob" var ptr *Person = &p ptr.Name += " LastName"
type Tree struct { value int left, right *Tree }
Declared as a variant of the ordinary function declaration, the extra type appears before the function name
type Point struct { X, Y float64 } func (p Point) Distance(q Point) float64 { return math.Hypot(q.X-p.X, q.Y-p.Y) } p := Point{1,1} q := Point{2,2} fmt.Println(p.Distance(q)) // 1.4142135623730951
p
is the method's receiver
Go typically uses short receiver names, eg: first letter of the type name
Why is it useful to use pointers as receivers?
func (p *Point) Scale(factor float64) { p.X *= factor p.Y *= factor }
Style: If one method for a type has a pointer receiver, then all should
Receivers can only be named types and pointers to named types
(Point) // ok (*Point) // ok type P *int (P) // not ok
Using a pointer receiver
p1 := &Point{1, 1} p1.Scale(4) fmt.Println(*p1) // {4, 4}
What happens here?
p1 := Point{1, 1} p1.Scale(4) fmt.Println(p)
Go does an implicit *p or &p conversion on variables
Go has a String() method to help with printing
func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) }
Works with:
fmt.Println(c) fmt.Println(c.String()) fmt.Printf("%v", c) fmt.Printf("%s", c)
type Point struct { X, Y float64 } type Circle struct { Point Radius int }
Any Circle can access the methods of Point
var c Circle c.X = 2 c.Y = 4 c.Scale(4) fmt.Printf("%+v\n", c) // {Point:{X:8 Y:16} Radius:0}
The methods of Point are promoted to Circle
How do we handle encapsulation?
Method value: binds a method to a receiver value
distanceFromP := p.Distance fmt.Println(distanceFromP(q))
Method Expression
distance := Point.Distance fmt.Println(distance(p, q))
Use go test
Applies to files that end in "_test.go"
Test function syntax
import "testing" func TestName(t *testing.T) { // }
Must begin with "Test", the name of the rest of the function must begin with a capital letter
func TestPalindromeSimple(t *testing.T) { if !IsPalindrome("racecar") { t.Error(`IsPalindrome("racecar") = false`) } } func TestIsPalindrome(t *testing.T) { var tests = []struct { input string want bool }{ {"", true}, {"a", true}, {"aa", true}, {"ab", false}, } for _, test := range tests { if got := IsPalindrome(test.input); got != test.want { t.Errorf("IsPalindrome(%q) = %v", test.input, got) } } }