Go does not have class implemenations. But class == struct types + methods in Go
- struct only holds the state, not the behavior
- method changes the state of struct types
Access level in a package
- capitalized fields, methods and functions are public
- lower case fields, methods and functions are package private
"Constructors" in Go
- declare lower case struct type, thus making it private
- and use a capitalized function
New
which return an object of this struct type - This is a factory pattern by default
Composition over Inheritence
- Go prefers embedding a struct inside the other
- Ask yourself what is inheritence?
- base class's data members are in the derived class ==> Go uses composition
- base class's non-private interfaces can be invoked inside derived class
- ===> Go uses interface and package-level access level control
- polymorphism ===> Go uses interface values such as
var x <Interface> = <Type>('hello world')
- so any interface can be associated with any type
- golang is very serious about variable types
var x int = 1
y := x
the compiler infers new variable y's type- which is the same as
x
's
- which is the same as
- all cast must be explicit (unlike C++)
- constant
const x = 1.1
- define your own
struct type
type MyFloat float64
type Person struct {
name string
age int
}
- struct embedding
- Composition over inheritance
// copied from https://flaviocopes.com/golang-is-go-object-oriented/
type Dog struct {
Animal // Composition by struct embedding
}
type Animal struct {
Age int
}
func (a *Animal) Move() {
fmt.Println("Animal moved")
}
func main() {
d := Dog{}
d.Age = 3 // Age automatically becomes part of Dog
d.Move() // call Animal's method directly
}
func needInt(x int) int { return x*10 + 1 }
func needInt(x int) (int, int) { return 1, 2 }
- bind the return variable
func needInt(x int) (x, y int) {
x++
y += x
return
}
- closure
adder
is bound to its own variablesum
func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}
x = adder() // sum == 0 now
x(1) // sum == 1 now
x(2) // sum == 3 now
- functions can be
- stored as struct fields
- passed as arguments to other functions
- returned from a function or methods
- there is no class in Go
- a method is a function with a receiver
// Abs() has v as its receiver
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
- methods can only be attached to a type in the same package where this type is defined
- Use pointer receiver to allow modification
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
See this Post
-
interface is a set of methods
-
interface type (
I interface{}
) is a typevar i I interface{} = "hello"
-
all types implement at least zero methods
- thus they are all of the empty interface type, i.e.
interface{}
- thus they are all of the empty interface type, i.e.
func DoSomething(v interface{}) {
// ...
}
will accept any parameter v
whatsoever. But they will convert any type
to the interface type.
- Example of a type
string
implementing an interfaceI
type I interface {
M()
}
// we can say: interface `I` holds the concrete type string
// or: M() has a receiver of type string
func (s string) M() { /* whatever */ }
// create an interface value
var i I = string("hello")
// we will be able to call the method
i.M()
-
a type can implement different interfaces
-
an interface can hold many different types
-
t, ok := i.(T)
checks if interface valuei
holds a typeT
ok == true
if yes; otherwise,ok == false
t
will be the underlying value if yes
-
an interface value
i
provides Polymorphism- for example, every type can have its own way to
fmt.Printf()
if- interface
Stringer
's methodString() string
holds this type func (t SomeType) String() string { return <string of t to display> }
- interface
- for example, every type can have its own way to
-
Polymorphism
func do(i interface{}) {
switch v := i.(type) {
case T: ...
case S: ...
default: ...
}
}
- built-in interface
type error interface {
Error() string
}
- the customized Error type implements an
error
interfacefunc (e MyError) Error() string { /* handle error */ }
- one extra statement before the if condition
if v := math.Pow(x, n); v < lim {
return v
}
// v is only in the scope of the if condition
- for loop can ignore end condition
for { /* forever */ }
switch
automatically addbreak
for everycase
(unlike C++)
- function executes after the current function returns
defer
functions are pushed to a stack- their executions order is the reversed push order
- No pointer operations!! ==> We do not need the
->
like in C++, always use.
to access fields. - No pointer operation does not imply no pointer
- there is no pass-by-reference in Go; one always pass by value (this can avoid many bugs)
- pointer saves the cost of copy in pass-by-value
type Vertex struct {
X, Y int
}
p := &Vertex{1, 2} // p is a pointer which allows modification of the struct
p.X = 100
var a [2]string
primes := [6]int{2, 3, 5, 7, 11, 13}
var s []int = primes[1:4]
- slices are like Python,
a := names[0:2]
a
is a reference of the arraylen(a)
is length of slicecap(a)
is the allocated memory starting froma[0]
- slice
x == nil
iflen(x) == 0
- create slice
a := make([]int, 5)
- slice of slices
board := [][]string{
[]string{"_", "_", "_"},
[]string{"_", "_", "_"},
[]string{"_", "_", "_"},
}
range
iterates through (index, value) pairs, like Python'senumerate()
for i := range pow { /* index i*/ }
for i, val := range pow { /* index i*/ }
var m map[<key type>]<value type>
- Insert by
m[key] = value
- Obtain value by
value = m[key]
- Check exist by
val, exit = m[key]
exit == false
ifkey
is not inm
- Check exist by
- Remove by
delete(m, key)
How to implement a local static
variable inside a function? Use Closure
func main() {
counter := newCounter()
counter() // return 1
counter() // return 2
}
// Here newCounter() returns an anonymous function
// which has access to n even after it exists
func newCounter() func() int {
n := 0
return func() int {
n += 1
return n
}
}
Run go test -v
at the package where the test file has a filename ending with _test.go
.
func TestSum(t *testing.T) {
t.Run("[1,2,3,4,5]", testSumFunc([]int{1, 2, 3, 4, 5}, 15))
t.Run("[1,2,3,4,-5]", testSumFunc([]int{1, 2, 3, 4, -5}, 5))
}
func testSumFunc(numbers []int, expected int) func(*testing.T) {
return func(t *testing.T) {
actual := Sum(numbers)
if actual != expected {
t.Error(fmt.Sprintf("Expected the sum of %v to be %d but instead got %d!", numbers, expected, actual))
}
}
}