Merge pull request #867 from ldynia/master

adding minor changes
This commit is contained in:
astaxie
2017-07-17 10:58:05 +08:00
committed by GitHub
6 changed files with 253 additions and 247 deletions

View File

@@ -21,13 +21,13 @@ For other languages there are many variables when it comes to writing code, ever
So to mitigate all the problems that Google faced with the current tools, they wrote a systems language called Go, which you are about to learn! There are many advantages to using golang, and there might be disadvantages too for every coin has both sides. But significant improvements in places like code formatting, since they designed the language in such a way that there won't be wars on how to format code, the gocode written by anyone in the world (assuming they know and use `gofmt`) will look exactly the same, this won't seem to matter until you work in a team! also when the company uses automated code review or some other fancy technique then in other languages which don't have strict and standard formatting rules then the code might get screwed up, but not in go! So to mitigate all the problems that Google faced with the current tools, they wrote a systems language called Go, which you are about to learn! There are many advantages to using golang, and there might be disadvantages too for every coin has both sides. But significant improvements in places like code formatting, since they designed the language in such a way that there won't be wars on how to format code, the gocode written by anyone in the world (assuming they know and use `gofmt`) will look exactly the same, this won't seem to matter until you work in a team! also when the company uses automated code review or some other fancy technique then in other languages which don't have strict and standard formatting rules then the code might get screwed up, but not in go!
Go was designed with concurrency in mind, please note that parallelism != concurrency, there is an amazing post by Rob Pike on the golang blog, blog.golang.org, you will find it there, it is worth a read. Go was designed with concurrency in mind, please note that parallelism != concurrency, there is an amazing post by Rob Pike on the golang blog, [www.blog.golang.org](https://blog.golang.org/concurrency-is-not-parallelism), you will find it there, it is worth a read.
Another very important change that go has brought in programming that I personally love is the concept of `GOPATH`, gone are the days when you had to create a folder called `code` and then create workspaces for eclipse and what not, now you have to keep one folder tree for go code and it'll be updated by the package manager automatically. Also under the code we are recommended to create folders with either a custom domain or the github domain, for example I created a task manager using golang so I created a set of folders Another very important change that go has brought in programming that I personally love is the concept of `GOPATH`, gone are the days when you had to create a folder called `code` and then create workspaces for eclipse and what not, now you have to keep one folder tree for go code and it'll be updated by the package manager automatically. Also under the code we are recommended to create folders with either a custom domain or the github domain, for example I created a task manager using golang so I created a set of folders
`~/go/src/github.com/thewhitetulip/Tasks` note: In *nix systems `~` stands for home directory, which is the windows equivalent of `C:\\Users\\username` `~/go/src/github.com/thewhitetulip/Tasks` note: In *nix systems `~` stands for home directory, which is the windows equivalent of `C:\\Users\\username`
now the `~/go/` is the universe for the gocode in your machine, it is just a significant improvement over other languages so we can store the code efficiently without hassles, it might seem strange at first, but it does make a lot of sense over the ridiculous package names some other languages use like reverse domains. now the `~/go/` is the universe for the gocode in your machine, it is just a significant improvement over other languages so we can store the code efficiently without hassles, it might seem strange at first, but it does make a lot of sense over the ridiculous package names some other languages use like reverse domains.
note: along with src there are two folders `pkg` which is for packages and `bin` which is for binary **Note:** Along with `src` there are two folders `pkg` which is for packages and `bin` which is for binary.
This `GOPATH` advantage isn't just restricted to storing code in particular folder, but when you have created five packages for your project then you don't have to import them like `"import ./db"`, you can give it `import "github.com/thewhitetulip/Tasks/db"`, so while doing a `go get` on my repo, the `go` tool will find the package from `github.com/...` path if it wasn't downloaded initially, it just standardizes a lot of screwed up things in the programming discipline. This `GOPATH` advantage isn't just restricted to storing code in particular folder, but when you have created five packages for your project then you don't have to import them like `"import ./db"`, you can give it `import "github.com/thewhitetulip/Tasks/db"`, so while doing a `go get` on my repo, the `go` tool will find the package from `github.com/...` path if it wasn't downloaded initially, it just standardizes a lot of screwed up things in the programming discipline.

View File

@@ -47,7 +47,7 @@ Well, I know this is still not simple enough for you. Let's see how we fix it.
*/ */
vname1, vname2, vname3 := v1, v2, v3 vname1, vname2, vname3 := v1, v2, v3
``` ```
Now it looks much better. Use `:=` to replace `var` and `type`, this is called a brief statement. But wait, it has one limitation: this form can only be used inside of functions. You will get compile errors if you try to use it outside of function bodies. Therefore, we usually use `var` to define global variables. Now it looks much better. Use `:=` to replace `var` and `type`, this is called a **short assignment**. It has one limitation: this form can only be used inside of a functions. You will get compile errors if you try to use it outside of function bodies. Therefore, we usually use `var` to define global variables.
`_` (blank) is a special variable name. Any value that is given to it will be ignored. For example, we give `35` to `b`, and discard `34`.( ***This example just show you how it works. It looks useless here because we often use this symbol when we get function return values.*** ) `_` (blank) is a special variable name. Any value that is given to it will be ignored. For example, we give `35` to `b`, and discard `34`.( ***This example just show you how it works. It looks useless here because we often use this symbol when we get function return values.*** )
```Go ```Go

View File

@@ -108,7 +108,7 @@ for index := 10; index>0; index-- {
``` ```
`for` can read data from `array`, `slice`, `map` and `string` when it is used together with `range`. `for` can read data from `array`, `slice`, `map` and `string` when it is used together with `range`.
```Go ```Go
for k,v:=range map { for k,v := range map {
fmt.Println("map's key:",k) fmt.Println("map's key:",k)
fmt.Println("map's val:",v) fmt.Println("map's val:",v)
} }
@@ -119,6 +119,14 @@ for _, v := range map{
fmt.Println("map's val:", v) fmt.Println("map's val:", v)
} }
``` ```
With go you can as well create an infinite loop, which is equivalent to `while true { ... }` in other languges.
```GO
for {
// yout logic
}
```
### switch ### switch
Sometimes you may find that you are using too many `if-else` statements to implement some logic, which may make it difficult to read and maintain in the future. This is the perfect time to use the `switch` statement to solve this problem. Sometimes you may find that you are using too many `if-else` statements to implement some logic, which may make it difficult to read and maintain in the future. This is the perfect time to use the `switch` statement to solve this problem.
@@ -210,9 +218,9 @@ func max(a, b int) int {
return a return a
} }
return b return b
} }
func main() { func main() {
x := 3 x := 3
y := 4 y := 4
z := 5 z := 5
@@ -224,8 +232,8 @@ func max(a, b int) int {
fmt.Printf("max(%d, %d) = %d\n", x, z, max_xz) fmt.Printf("max(%d, %d) = %d\n", x, z, max_xz)
fmt.Printf("max(%d, %d) = %d\n", y, z, max(y, z)) // call function here fmt.Printf("max(%d, %d) = %d\n", y, z, max(y, z)) // call function here
} }
``` ```
In the above example, there are two arguments in the function `max`, their types are both `int` so the first type can be omitted. For instance, `a, b int` instead of `a int, b int`. The same rules apply for additional arguments. Notice here that `max` only has one return value, so we only need to write the type of its return value -this is the short form of writing it. In the above example, there are two arguments in the function `max`, their types are both `int` so the first type can be omitted. For instance, `a, b int` instead of `a int, b int`. The same rules apply for additional arguments. Notice here that `max` only has one return value, so we only need to write the type of its return value -this is the short form of writing it.
### Multi-value return ### Multi-value return
@@ -392,21 +400,14 @@ import "fmt"
type testInt func(int) bool // define a function type of variable type testInt func(int) bool // define a function type of variable
func isOdd(integer int) bool { func isOdd(integer int) bool {
if integer%2 == 0 { return integer%2 != 0
return false
}
return true
} }
func isEven(integer int) bool { func isEven(integer int) bool {
if integer%2 == 0 { return integer%2 == 0
return true
}
return false
} }
// pass the function `f` as an argument to another function // pass the function `f` as an argument to another function
func filter(slice []int, f testInt) []int { func filter(slice []int, f testInt) []int {
var result []int var result []int
for _, value := range slice { for _, value := range slice {
@@ -417,16 +418,18 @@ func filter(slice []int, f testInt) []int {
return result return result
} }
var slice = []int{1, 2, 3, 4, 5, 7}
func main() { func main() {
slice := []int{1, 2, 3, 4, 5, 7} odd := filter(slice, isOdd)
fmt.Println("slice = ", slice) even := filter(slice, isEven)
odd := filter(slice, isOdd) // use function as values
fmt.Println("slice = ", slice)
fmt.Println("Odd elements of slice are: ", odd) fmt.Println("Odd elements of slice are: ", odd)
even := filter(slice, isEven)
fmt.Println("Even elements of slice are: ", even) fmt.Println("Even elements of slice are: ", even)
} }
``` ```
It's very useful when we use interfaces. As you can see `testInt` is a variable that has a function as type and the returned values and arguments of `filter` are the same as those of `testInt`. Therefore, we can have complex logic in our programs, while maintaining flexibility in our code. It's very useful when we use interfaces. As you can see `testInt` is a variable that has a function as type and the returned values and arguments of `filter` are the same as those of `testInt`. Therefore, we can have complex logic in our programs, while maintaining flexibility in our code.
### Panic and Recover ### Panic and Recover

View File

@@ -44,6 +44,7 @@ P := person{age:24, name:"Bob"}
P := struct{name string; age int}{"Amy",18} P := struct{name string; age int}{"Amy",18}
``` ```
Let's see a complete example. Let's see a complete example.
```Go ```Go
package main package main
@@ -55,8 +56,8 @@ type person struct {
age int age int
} }
// compare the age of two people, then return the older person and differences of age
// struct is passed by value // struct is passed by value
// compare the age of two people, then return the older person and differences of age
func Older(p1, p2 person) (person, int) { func Older(p1, p2 person) (person, int) {
if p1.age > p2.age { if p1.age > p2.age {
return p1, p1.age - p2.age return p1, p1.age - p2.age
@@ -67,13 +68,8 @@ func Older(p1, p2 person) (person, int) {
func main() { func main() {
var tom person var tom person
// initialization
tom.name, tom.age = "Tom", 18 tom.name, tom.age = "Tom", 18
// initialize two values by format "field:value"
bob := person{age: 25, name: "Bob"} bob := person{age: 25, name: "Bob"}
// initialize two values with order
paul := person{"Paul", 43} paul := person{"Paul", 43}
tb_Older, tb_diff := Older(tom, bob) tb_Older, tb_diff := Older(tom, bob)
@@ -81,12 +77,9 @@ func main() {
bp_Older, bp_diff := Older(bob, paul) bp_Older, bp_diff := Older(bob, paul)
fmt.Printf("Of %s and %s, %s is older by %d years\n", tom.name, bob.name, tb_Older.name, tb_diff) fmt.Printf("Of %s and %s, %s is older by %d years\n", tom.name, bob.name, tb_Older.name, tb_diff)
fmt.Printf("Of %s and %s, %s is older by %d years\n", tom.name, paul.name, tp_Older.name, tp_diff) fmt.Printf("Of %s and %s, %s is older by %d years\n", tom.name, paul.name, tp_Older.name, tp_diff)
fmt.Printf("Of %s and %s, %s is older by %d years\n", bob.name, paul.name, bp_Older.name, bp_diff) fmt.Printf("Of %s and %s, %s is older by %d years\n", bob.name, paul.name, bp_Older.name, bp_diff)
} }
``` ```
### embedded fields in struct ### embedded fields in struct
@@ -112,7 +105,7 @@ type Student struct {
} }
func main() { func main() {
// initialize a student // instantiate and initialize a student
mark := Student{Human{"Mark", 25, 120}, "Computer Science"} mark := Student{Human{"Mark", 25, 120}, "Computer Science"}
// access fields // access fields
@@ -120,17 +113,16 @@ func main() {
fmt.Println("His age is ", mark.age) fmt.Println("His age is ", mark.age)
fmt.Println("His weight is ", mark.weight) fmt.Println("His weight is ", mark.weight)
fmt.Println("His specialty is ", mark.specialty) fmt.Println("His specialty is ", mark.specialty)
// modify notes
// modify mark's specialty
mark.specialty = "AI" mark.specialty = "AI"
fmt.Println("Mark changed his specialty") fmt.Println("Mark changed his specialty")
fmt.Println("His specialty is ", mark.specialty) fmt.Println("His specialty is ", mark.specialty)
// modify age
fmt.Println("Mark become old") fmt.Println("Mark become old. He is not an athlete anymore")
mark.age = 46 mark.age = 46
fmt.Println("His age is", mark.age)
// modify weight
fmt.Println("Mark is not an athlet anymore")
mark.weight += 60 mark.weight += 60
fmt.Println("His age is", mark.age)
fmt.Println("His weight is", mark.weight) fmt.Println("His weight is", mark.weight)
} }
@@ -202,15 +194,15 @@ type Human struct {
} }
type Employee struct { type Employee struct {
Human // embedded field Human Human
specialty string specialty string
phone string // phone in employee phone string // phone in employee
} }
func main() { func main() {
Bob := Employee{Human{"Bob", 34, "777-444-XXXX"}, "Designer", "333-222"} Bob := Employee{Human{"Bob", 34, "777-444-XXXX"}, "Designer", "333-222"}
fmt.Println("Bob's work phone is:", Bob.phone) fmt.Println("Bob's work phone is:", Bob.phone)
// access phone field in Human
fmt.Println("Bob's personal phone is:", Bob.Human.phone) fmt.Println("Bob's personal phone is:", Bob.Human.phone)
} }

View File

@@ -1,6 +1,6 @@
# Object-oriented # Object-oriented
We talked about functions and structs in the last two sections, but did you ever consider using functions as fields of a struct? In this section, I will introduce you to another form of function that has a receiver, which is called `method`. We talked about functions and structs in the last two sections, but did you ever consider using functions as fields of a struct? In this section, I will introduce you to another form of function that has a receiver, which is called a `method`.
## method ## method
@@ -36,9 +36,9 @@ Figure 2.8 Relationship between function and struct
Obviously that's not cool. Also, the area should really be the property of a circle or rectangle. Obviously that's not cool. Also, the area should really be the property of a circle or rectangle.
For those reasons, we have the `method` concept. `method` is affiliated with type. It has the same syntax as functions do except for an additional parameter after the `func` keyword called the `receiver`, which is the main body of that method. This is where a `method` comes to play. The `method` is a function affiliated with a type. It has similar syntax as function except, after the `func` keyword has a parameter called the `receiver`, which is the main body of that method.
Using the same example, `Rectangle.area()` belongs directly to rectangle, instead of as a peripheral function. More specifically, `length`, `width` and `area()` all belong to rectangle. Using the same example, `Rectangle.Area()` belongs directly to rectangle, instead of as a peripheral function. More specifically, `length`, `width` and `Area()` all belong to rectangle.
As Rob Pike said. As Rob Pike said.
@@ -57,35 +57,37 @@ import (
"math" "math"
) )
type Rectangle struct {
width, height float64
}
type Circle struct { type Circle struct {
radius float64 radius float64
} }
func (r Rectangle) area() float64 { type Rectangle struct {
return r.width * r.height width, height float64
} }
func (c Circle) area() float64 { // method
func (c Circle) Area() float64 {
return c.radius * c.radius * math.Pi return c.radius * c.radius * math.Pi
} }
func main() { // method
r1 := Rectangle{12, 2} func (r Rectangle) Area() float64 {
r2 := Rectangle{9, 4} return r.width * r.height
c1 := Circle{10}
c2 := Circle{25}
fmt.Println("Area of r1 is: ", r1.area())
fmt.Println("Area of r2 is: ", r2.area())
fmt.Println("Area of c1 is: ", c1.area())
fmt.Println("Area of c2 is: ", c2.area())
} }
func main() {
c1 := Circle{10}
c2 := Circle{25}
r1 := Rectangle{9, 4}
r2 := Rectangle{12, 2}
fmt.Println("Area of c1 is: ", c1.Area())
fmt.Println("Area of c2 is: ", c2.Area())
fmt.Println("Area of r1 is: ", r1.Area())
fmt.Println("Area of r2 is: ", r2.Area())
}
``` ```
Notes for using methods. Notes for using methods.
- If the name of methods are the same but they don't share the same receivers, they are not the same. - If the name of methods are the same but they don't share the same receivers, they are not the same.
@@ -96,7 +98,7 @@ Notes for using methods.
Figure 2.9 Methods are different in different structs Figure 2.9 Methods are different in different structs
In the example above, the area() methods belong to both Rectangle and Circle respectively, so the receivers are Rectangle and Circle. In the example above, the Area() methods belong to both Rectangle and Circle respectively, so the receivers are Rectangle and Circle.
One thing that's worth noting is that the method with a dotted line means the receiver is passed by value, not by reference. The difference between them is that a method can change its receiver's values when the receiver is passed by reference, and it gets a copy of the receiver when the receiver is passed by value. One thing that's worth noting is that the method with a dotted line means the receiver is passed by value, not by reference. The difference between them is that a method can change its receiver's values when the receiver is passed by reference, and it gets a copy of the receiver when the receiver is passed by value.
@@ -107,11 +109,10 @@ Use the following format to define a customized type.
type typeName typeLiteral type typeName typeLiteral
``` ```
Examples of customized types: Examples of customized types:
```Go ```Go
type ages int type age int
type money float32 type money float32
type months map[string]int type months map[string]int
m := months { m := months {
@@ -121,6 +122,7 @@ m := months {
"December":31, "December":31,
} }
``` ```
I hope that you know how to use customized types now. Similar to `typedef` in C, we use `ages` to substitute `int` in the above example. I hope that you know how to use customized types now. Similar to `typedef` in C, we use `ages` to substitute `int` in the above example.
Let's get back to talking about `method`. Let's get back to talking about `method`.
@@ -139,23 +141,24 @@ const (
YELLOW YELLOW
) )
type Color byte
type Box struct { type Box struct {
width, height, depth float64 width, height, depth float64
color Color color Color
} }
type Color byte
type BoxList []Box //a slice of boxes type BoxList []Box //a slice of boxes
// method
func (b Box) Volume() float64 { func (b Box) Volume() float64 {
return b.width * b.height * b.depth return b.width * b.height * b.depth
} }
// method with a pointer receiver
func (b *Box) SetColor(c Color) { func (b *Box) SetColor(c Color) {
b.color = c b.color = c
} }
// method
func (bl BoxList) BiggestsColor() Color { func (bl BoxList) BiggestsColor() Color {
v := 0.00 v := 0.00
k := Color(WHITE) k := Color(WHITE)
@@ -168,12 +171,14 @@ func (bl BoxList) BiggestsColor() Color {
return k return k
} }
// method
func (bl BoxList) PaintItBlack() { func (bl BoxList) PaintItBlack() {
for i, _ := range bl { for i, _ := range bl {
bl[i].SetColor(BLACK) bl[i].SetColor(BLACK)
} }
} }
// method
func (c Color) String() string { func (c Color) String() string {
strings := []string{"WHITE", "BLACK", "BLUE", "RED", "YELLOW"} strings := []string{"WHITE", "BLACK", "BLUE", "RED", "YELLOW"}
return strings[c] return strings[c]
@@ -194,14 +199,14 @@ func main() {
fmt.Println("The color of the last one is", boxes[len(boxes)-1].color.String()) fmt.Println("The color of the last one is", boxes[len(boxes)-1].color.String())
fmt.Println("The biggest one is", boxes.BiggestsColor().String()) fmt.Println("The biggest one is", boxes.BiggestsColor().String())
fmt.Println("Let's paint them all black") // Let's paint them all black
boxes.PaintItBlack() boxes.PaintItBlack()
fmt.Println("The color of the second one is", boxes[1].color.String())
fmt.Println("The color of the second one is", boxes[1].color.String())
fmt.Println("Obviously, now, the biggest one is", boxes.BiggestsColor().String()) fmt.Println("Obviously, now, the biggest one is", boxes.BiggestsColor().String())
} }
``` ```
We define some constants and customized types. We define some constants and customized types.
- Use `Color` as alias of `byte`. - Use `Color` as alias of `byte`.
@@ -210,11 +215,11 @@ We define some constants and customized types.
Then we defined some methods for our customized types. Then we defined some methods for our customized types.
- Volume() uses Box as its receiver and returns the volume of Box. - `Volume()` uses Box as its receiver and returns the volume of Box.
- SetColor(c Color) changes Box's color. - `SetColor(`c Color) changes Box's color.
- BiggestsColor() returns the color which has the biggest volume. - `BiggestsColor()` returns the color which has the biggest volume.
- PaintItBlack() sets color for all Box in BoxList to black. - `PaintItBlack()` sets color for all Box in BoxList to black.
- String() use Color as its receiver, returns the string format of color name. - `String()` use Color as its receiver, returns the string format of color name.
Is it much clearer when we use words to describe our requirements? We often write our requirements before we start coding. Is it much clearer when we use words to describe our requirements? We often write our requirements before we start coding.
@@ -224,7 +229,7 @@ Let's take a look at `SetColor` method. Its receiver is a pointer of Box. Yes, y
If we see that a receiver is the first argument of a method, it's not hard to understand how it works. If we see that a receiver is the first argument of a method, it's not hard to understand how it works.
You might be asking why we aren't using `(*b).Color=c` instead of `b.Color=c` in the SetColor() method. Either one is OK here because Go knows how to interpret the assignment. Do you think Go is more fascinating now? You might be asking why we aren't using `(*b).Color=c` instead of `b.Color=c` in the `SetColor()` method. Either one is OK here because Go knows how to interpret the assignment. Do you think Go is more fascinating now?
You may also be asking whether we should use `(&bl[i]).SetColor(BLACK)` in `PaintItBlack` because we pass a pointer to `SetColor`. Again, either one is OK because Go knows how to interpret it! You may also be asking whether we should use `(&bl[i]).SetColor(BLACK)` in `PaintItBlack` because we pass a pointer to `SetColor`. Again, either one is OK because Go knows how to interpret it!
@@ -258,13 +263,12 @@ func (h *Human) SayHi() {
} }
func main() { func main() {
mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"}
sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"} sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}
mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"}
mark.SayHi()
sam.SayHi() sam.SayHi()
mark.SayHi()
} }
``` ```
### Method overload ### Method overload
@@ -300,11 +304,11 @@ func (e *Employee) SayHi() {
} }
func main() { func main() {
mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"}
sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"} sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}
mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"}
mark.SayHi()
sam.SayHi() sam.SayHi()
mark.SayHi()
} }
``` ```

View File

@@ -20,7 +20,6 @@ This combination of methods is called an interface and is implemented by both St
An interface defines a set of methods, so if a type implements all the methods we say that it implements the interface. An interface defines a set of methods, so if a type implements all the methods we say that it implements the interface.
```Go ```Go
type Human struct { type Human struct {
name string name string
age int age int
@@ -39,33 +38,7 @@ type Employee struct {
money float32 money float32
} }
func (h *Human) SayHi() { // define interfaces
fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}
func (h *Human) Sing(lyrics string) {
fmt.Println("La la, la la la, la la la la la...", lyrics)
}
func (h *Human) Guzzle(beerStein string) {
fmt.Println("Guzzle Guzzle Guzzle...", beerStein)
}
// Employee overloads Sayhi
func (e *Employee) SayHi() {
fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
e.company, e.phone) //Yes you can split into 2 lines here.
}
func (s *Student) BorrowMoney(amount float32) {
s.loan += amount // (again and again and...)
}
func (e *Employee) SpendSalary(amount float32) {
e.money -= amount // More vodka please!!! Get me through the day!
}
// define interface
type Men interface { type Men interface {
SayHi() SayHi()
Sing(lyrics string) Sing(lyrics string)
@@ -84,6 +57,31 @@ type ElderlyGent interface {
SpendSalary(amount float32) SpendSalary(amount float32)
} }
func (h *Human) SayHi() {
fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}
func (h *Human) Sing(lyrics string) {
fmt.Println("La la, la la la, la la la la la...", lyrics)
}
func (h *Human) Guzzle(beerStein string) {
fmt.Println("Guzzle Guzzle Guzzle...", beerStein)
}
// Employee overloads SayHi
func (e *Employee) SayHi() {
fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
e.company, e.phone) //Yes you can split into 2 lines here.
}
func (s *Student) BorrowMoney(amount float32) {
s.loan += amount // (again and again and...)
}
func (e *Employee) SpendSalary(amount float32) {
e.money -= amount // More vodka please!!! Get me through the day!
}
``` ```
We know that an interface can be implemented by any type, and one type can implement many interfaces simultaneously. We know that an interface can be implemented by any type, and one type can implement many interfaces simultaneously.
@@ -117,25 +115,28 @@ type Employee struct {
money float32 money float32
} }
func (h Human) SayHi() {
fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}
func (h Human) Sing(lyrics string) {
fmt.Println("La la la la...", lyrics)
}
func (e Employee) SayHi() {
fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
e.company, e.phone) //Yes you can split into 2 lines here.
}
// Interface Men implemented by Human, Student and Employee // Interface Men implemented by Human, Student and Employee
type Men interface { type Men interface {
SayHi() SayHi()
Sing(lyrics string) Sing(lyrics string)
} }
// method
func (h Human) SayHi() {
fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}
// method
func (h Human) Sing(lyrics string) {
fmt.Println("La la la la...", lyrics)
}
// method
func (e Employee) SayHi() {
fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
e.company, e.phone) //Yes you can split into 2 lines here.
}
func main() { func main() {
mike := Student{Human{"Mike", 25, "222-222-XXX"}, "MIT", 0.00} mike := Student{Human{"Mike", 25, "222-222-XXX"}, "MIT", 0.00}
paul := Student{Human{"Paul", 26, "111-222-XXX"}, "Harvard", 100} paul := Student{Human{"Paul", 26, "111-222-XXX"}, "Harvard", 100}
@@ -167,7 +168,6 @@ func main() {
value.SayHi() value.SayHi()
} }
} }
``` ```
An interface is a set of abstract methods, and can be implemented by non-interface types. It cannot therefore implement itself. An interface is a set of abstract methods, and can be implemented by non-interface types. It cannot therefore implement itself.
@@ -176,20 +176,24 @@ An interface is a set of abstract methods, and can be implemented by non-interfa
An empty interface is an interface that doesn't contain any methods, so all types implement an empty interface. This fact is very useful when we want to store all types at some point, and is similar to void* in C. An empty interface is an interface that doesn't contain any methods, so all types implement an empty interface. This fact is very useful when we want to store all types at some point, and is similar to void* in C.
```Go ```Go
// define a as empty interface // define a as empty interface
var a interface{} var void interface{}
var i int = 5
// vars
i := 5
s := "Hello world" s := "Hello world"
// a can store value of any type // a can store value of any type
a = i void = i
a = s void = s
``` ```
If a function uses an empty interface as its argument type, it can accept any type; if a function uses empty interface as its return value type, it can return any type. If a function uses an empty interface as its argument type, it can accept any type; if a function uses empty interface as its return value type, it can return any type.
### Method arguments of an interface ### Method arguments of an interface
Any variable can be used in an interface. So how can we use this feature to pass any type of variable to a function? Any variable can be used in an interface. So how can we use this feature to pass any type of variable to a function?
For example we use fmt.Println a lot, but have you ever noticed that it can accept any type of argument? Looking at the open source code of fmt, we see the following definition. For example we use `fmt.Println` a lot, but have you ever noticed that it can accept any type of argument? Looking at the open source code of `fmt`, we see the following definition.
```Go ```Go
type Stringer interface { type Stringer interface {
String() string String() string
@@ -210,7 +214,7 @@ type Human struct {
phone string phone string
} }
// Human implemented fmt.Stringer // Human implements fmt.Stringer
func (h Human) String() string { func (h Human) String() string {
return "Name:" + h.name + ", Age:" + strconv.Itoa(h.age) + " years, Contact:" + h.phone return "Name:" + h.name + ", Age:" + strconv.Itoa(h.age) + " years, Contact:" + h.phone
} }
@@ -219,8 +223,8 @@ func main() {
Bob := Human{"Bob", 39, "000-7777-XXX"} Bob := Human{"Bob", 39, "000-7777-XXX"}
fmt.Println("This Human is : ", Bob) fmt.Println("This Human is : ", Bob)
} }
``` ```
Looking back to the example of Box, you will find that Color implements interface Stringer as well, so we are able to customize the print format. If we don't implement this interface, fmt.Println prints the type with its default format. Looking back to the example of Box, you will find that Color implements interface Stringer as well, so we are able to customize the print format. If we don't implement this interface, fmt.Println prints the type with its default format.
```Go ```Go
fmt.Println("The biggest one is", boxes.BiggestsColor().String()) fmt.Println("The biggest one is", boxes.BiggestsColor().String())
@@ -362,7 +366,7 @@ type ReadWriter interface {
``` ```
### Reflection ### Reflection
Reflection in Go is used for determining information at runtime. We use the `reflect` package, and this official [article](http://golang.org/doc/articles/laws_of_reflection.html) explains how reflect works in Go. Reflection in Go is used for determining information at runtime. We use the `reflect` package, and [The Laws of Reflection](http://golang.org/doc/articles/laws_of_reflection.html) post explains how reflect works in Go.
There are three steps involved when using reflect. First, we need to convert an interface to reflect types (reflect.Type or reflect.Value, this depends on the situation). There are three steps involved when using reflect. First, we need to convert an interface to reflect types (reflect.Type or reflect.Value, this depends on the situation).
```Go ```Go
@@ -372,10 +376,13 @@ v := reflect.ValueOf(i) // get actual value in type i, and use v to change its
After that, we can convert the reflected types to get the values that we need. After that, we can convert the reflected types to get the values that we need.
```Go ```Go
var x float64 = 3.4 var x float64 = 3.4
t := reflect.TypeOf(x)
v := reflect.ValueOf(x) v := reflect.ValueOf(x)
fmt.Println("type:", v.Type())
fmt.Println("type:", t)
fmt.Println("value:", v)
fmt.Println("kind is float64:", v.Kind() == reflect.Float64) fmt.Println("kind is float64:", v.Kind() == reflect.Float64)
fmt.Println("value:", v.Float())
``` ```
Finally, if we want to change the values of the reflected types, we need to make it modifiable. As discussed earlier, there is a difference between pass by value and pass by reference. The following code will not compile. Finally, if we want to change the values of the reflected types, we need to make it modifiable. As discussed earlier, there is a difference between pass by value and pass by reference. The following code will not compile.
```Go ```Go