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 = 0Cannot 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 errorWe 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)
}
}
}