繼Golang學習系列第二天之後,今天開始學習資料型別之高階型別: 派生型別。
學過java的人都知道,java其實就8種基本型別:byte、short、int、long、float、double、char、boolean,但它有引用資料型別:字串、陣列、集合、類、介面等。
而golang也有這樣的劃分,基本型別(Golang學習系列第二天已學過)和派生型別(不叫引用型別),派生型別有以下幾種:陣列型別、切片型別、Map型別、結構體型別(struct)、指標型別(Pointer)、函式型別、介面型別(interface)、Channel 型別。
陣列是具有相同資料型別的元素序列。 陣列在宣告中定義了固定的長度,因此不能擴充套件超過該長度。 陣列宣告為
var variable_name [SIZE] variable_type
讓我們以程式碼舉例如下
package main
import "fmt"
func main() {
var city [5]string
city[0] = "北京"
city[1] = "上海"
city[2] = "廣州"
city[3] = "深圳"
city[4] = "濮陽"
fmt.Println(city[0], city[1], city[2], city[3], city[4])
fmt.Println(city)
}
將變數city宣告為5個字串的陣列,執行輸出
不過也可以在宣告陣列時設定陣列條目,看簡潔版
package main
import "fmt"
func main() {
var city = [5]string{ "北京", "上海","廣州","深圳","濮陽"}
fmt.Println(city[0], city[1], city[2], city[3], city[4])
fmt.Println(city)
//簡寫版
othercity := [5]string{ "北京", "上海","廣州", "深圳","濮陽"}
fmt.Printf("%q", othercity)
}
甚至,當您傳遞值時,可以使用省略號來使用隱式長度
package main
import "fmt"
func main() {
var city = [5]string{ "北京", "上海","廣州","深圳","濮陽"}
fmt.Println(city[0], city[1], city[2], city[3], city[4])
fmt.Println(city)
//簡寫版
othercity := [5]string{ "北京", "上海","廣州", "深圳","濮陽"}
fmt.Printf("%q\n", othercity)
//隱士長度
other_city := [...]string{ "北京", "上海","廣州", "深圳","濮陽"}
fmt.Printf("%q", other_city)
}
以不同方式列印陣列
注意我們使用帶Printf的fmt包以及如何使用%q“動詞”來列印每個引用的元素。
如果我們使用Println或%s動詞,我們將得到不同的結果
package main
import "fmt"
func main() {
//不同方式列印陣列
other_city := [...]string{ "北京", "上海","廣州", "深圳","濮陽"}
fmt.Println("不同方式列印陣列")
fmt.Println(other_city)
fmt.Printf("%s\n", other_city)
fmt.Printf("%q", other_city)
}
多維陣列
您還可以建立多維陣列,示例程式碼:
package main
import "fmt"
func main() {
var multi [2][3]int
for i := 0; i < 2; i++ {
for j := 0; j < 3; j++ {
multi[i][j] = i + j
}
}
fmt.Println("二維陣列: ", multi)
}
切片包裝陣列可為資料序列提供更通用,更強大和更方便的介面。 除了具有明確維數的項(例如轉換矩陣)外,Go中的大多數陣列程式設計都是使用切片而不是簡單陣列完成的。
切片包含對基礎陣列的引用,如果您將一個切片分配給另一個,則兩個切片都引用同一陣列。 如果函式採用slice引數,則對slice的元素所做的更改將對呼叫者可見,這類似於將指標傳遞給基礎陣列。
切片指向值陣列,並且還包含長度。 切片可以調整大小,因為它們只是另一個資料結構之上的包裝。
例如, []T is a slice with elements of type T 表示[] T是具有T型別元素的切片。
舉個小例子
package main
import "fmt"
func main() {
var city = []string{ "北京", "上海","廣州","深圳","濮陽"}
fmt.Println(city[0], city[1], city[2], city[3], city[4])
fmt.Println(city)
}
2.1 切分切片
可以對切片進行切片,以建立一個指向相同陣列的新切片值。表示式為
slice[start:end]
表示計算從start到end-1(含)的元素的一部分。
注意:start和end是表示索引的整數。
下面舉個小demo
package main
import "fmt"
func main() {
var city = []string{ "北京", "上海","廣州","深圳","濮陽","鄭洲"}
fmt.Println(city)
fmt.Println(city[1:4])
// missing low index implies 0
fmt.Println(city[:3])
// [2 3 5]
// missing high index implies len(s)
fmt.Println(city[4:])
}
2.2 製造切片
除了透過立即傳遞值(切片文字)來建立切片之外,還可以使用make關鍵字建立切片。 您建立一個特定長度的空切片,然後填充每個條目。
package main
import "fmt"
func main() {
fmt.Println("準備用make方式建立切片")
cities := make([]string, 3)
cities[0] = "鄭洲"
cities[1] = "濮陽"
cities[2] = "安陽"
fmt.Printf("%q", cities)
}
它透過分配清零的陣列並返回引用該陣列的切片來工作。
2.3 附加到切片
可以透過append方式附加值到切片中
package main
import "fmt"
func main() {
//附加到切片
other_cities := []string{}
other_cities = append(other_cities, "濮陽")
fmt.Println(other_cities)
}
也可以同時附加多個值到切片中,示例程式碼同時包括兩個城市鄭洲和濮陽
package main
import "fmt"
func main() {
//附加多值到切片
other_cities := []string{}
other_cities = append(other_cities, "鄭洲","濮陽")
fmt.Println(other_cities)
}
甚至您還可以使用省略號將切片附加到另一個切片
package main
import "fmt"
func main() {
fmt.Println("準備用make方式建立切片")
cities := make([]string, 3)
cities[0] = "鄭洲"
cities[1] = "濮陽"
cities[2] = "安陽"
fmt.Printf("%q\n", cities)
//附加切片到切片
other_cities := []string{"南京"}
other_cities = append(other_cities, cities...)
fmt.Println(other_cities)
}
注意: 省略號是該語言的內建功能,這意味著該元素是一個集合。 我們無法將字串型別([] string)型別的元素附加到字串切片中,只能附加字串。 但是,在切片後使用省略號(…),表示要附加切片的每個元素。 因為我們要從另一個片段追加字串,所以編譯器將接受操作,因為型別是匹配的。
您顯然無法將[] int型別的切片附加到[] string型別的另一個切片中。
2.4 複製切片
切片也可以複製。 在這裡,我們建立一個與other_cities長度相同的空切片copycities,並從other_cities複製到copycities中。
package main
import "fmt"
func main() {
fmt.Println("準備用make方式建立切片")
cities := make([]string, 3)
cities[0] = "鄭洲"
cities[1] = "濮陽"
cities[2] = "安陽"
fmt.Printf("%q\n", cities)
//附加多值到切片
other_cities := []string{"南京"}
other_cities = append(other_cities, cities...)
fmt.Println(other_cities)
//複製切片
fmt.Println("準備用copy方式建立切片")
copycities := make([]string, len(other_cities))
copy(copycities, other_cities)
fmt.Println("copycities:", copycities)
}
2.5 切片長度
您可以隨時使用len檢查切片的長度。
package main
import "fmt"
func main() {
fmt.Println("準備用make方式建立切片")
cities := make([]string, 3)
cities[0] = "鄭洲"
cities[1] = "濮陽"
cities[2] = "安陽"
fmt.Printf("%q\n", cities)
//附加多值到切片
other_cities := []string{"南京"}
other_cities = append(other_cities, cities...)
fmt.Println(other_cities)
//列印切片長度
fmt.Println(len(cities))
countries := make([]string, 42)
fmt.Println(len(countries))
}
2.6 零片
切片的零值為nil。 無切片的長度和容量為0。
package main
import "fmt"
func main() {
//零片
var temp []int
fmt.Println(temp, len(temp), cap(temp))
if temp == nil {
fmt.Println("nil!")
}
}
Map 是一種無序的鍵值對的集合,Map 最重要的一點是透過 key 來快速檢索資料,key 類似於索引,指向資料的值。
Map 是一種集合,所以我們可以像迭代陣列和切片那樣迭代它。不過,Map 是無序的,我們無法決定它的返回順序,這是因為 Map 是使用 hash 表來實現的。
package main
import "fmt"
func main() {
users := map[string]int{
"admin": 0,
"donguangming": 1,
"student": 2,
}
fmt.Printf("%#v", users)
}
當不使用上述map時,使用前必須使用make(不是新的)建立map。 nil對映為空,無法分配給它。
package main
import "fmt"
type User struct {
name string
age int
city string
}
var user map[string]User
func main() {
//透過make建立
user = make(map[string]User)
user["dgm"] = User{"董廣明", 99, "南京"}
fmt.Println(user["dgm"])
}
3.1 操作map
3.1.1 在map中插入或更新元素
m[key] = elem
3.1.2 查詢一個元素
elem = m[key]
3.1.3 刪除一個元素
delete(m, key)
3.1.4 測試鍵是否存在並且有值
elem, ok = m[key]
如果鍵在m中,則確定為true。 如果不是,則ok為false,elem為map元素型別的零值。 同樣,從map中讀取時(如果沒有按鍵)則結果是map元素型別的零值。
綜上合起來程式碼如下
package main
import "fmt"
type User struct {
name string
age int
city string
}
var user map[string]User
func main() {
user = make(map[string]User)
//賦值
user["dgm"] = User{"董廣明", 99, "南京"}
fmt.Println("user:", user)
//查詢
fmt.Println(user["dgm"])
//刪除
delete(user, "dgm")
fmt.Println("此時user:", user)
//測試鍵
_, u := user["dgm"]
fmt.Println("u:", u)
var users = map[string]User{
"dmg": {"董廣明", 99, "南京"},
"dongguangming": {"董廣明", 88, "南京"},
}
fmt.Println(users)
}
3.2 map迴圈
如何在map上進行迭代?您可以使用迴圈範圍來迭代map, 因為對映是無序集合,所以此迴圈的值可能會有所不同。
package main
import "fmt"
type User struct {
name string
age int
city string
}
var user map[string]User
func main() {
var users = map[string]User{
"dmg": {"董廣明", 99, "南京"},
"dongguangming": {"董廣明", 88, "南京"},
}
fmt.Println(users)
/*使用鍵輸出map鍵值 */
for username := range users {
fmt.Println(username, "使用者=", users [username])
}
}
學過C(上學時第一門程式語言就是C)的人都知道, 指標是存放值記憶體地址的地方, 指標由*定義,根據資料型別定義指標,go也是這麼玩的,宣告格式如下
var var_name *var-type
var-type 為指標型別,var_name 為指標變數名,* 號用於指定變數是作為一個指標。
&運算子可用於獲取變數的地址。比如
var ap *int
a := 12
ap = &a
而指標指向的值可以使用*運算子進行訪問,示例如下
package main
import "fmt"
func main() {
var age int= 99 /* 宣告實際變數 */
var ip *int /* 宣告指標變數 */
ip = &age /* 指標變數的儲存地址 */
fmt.Printf("age 變數的地址是: %x\n", &age )
/* 指標變數的儲存地址 */
fmt.Printf("ip 變數儲存的指標地址: %x\n", ip )
/* 使用指標訪問值 */
fmt.Printf("*ip指標變數的值: %d\n", *ip )
}
4.1 空指標
當一個指標被定義後沒有分配到任何變數時,它的值為 nil,nil 指標也稱為空指標。
nil在概念上和其它程式語言的null、None、nil、NULL一樣,都指代零值或空值。
一個指標變數通常縮寫為 ptr。空指標判斷方式:
if(ptr != nil) /* ptr 不是空指標 */
if(ptr == nil) /* ptr 是空指標 */
綜上示例程式碼如下
package main
import "fmt"
func main() {
var age int =99 /* 宣告實際變數 */
var ptr *int /* 宣告指標變數 */
var other_ptr *int /* 宣告指標變數 */
ptr = &age /* 指標變數的儲存地址 */
fmt.Printf("age 變數的地址是: %x\n", &age )
/* 使用指標訪問值 */
fmt.Printf("*ptr指標變數的值: %d\n", *ptr )
if(ptr != nil) {
/* ptr 不是空指標 */
fmt.Printf("ptr指標變數儲存的指標地址: %x\n", ptr )
}
if(other_ptr == nil) {
/* other_ptr 是空指標 */
fmt.Printf("other_ptr指標變數儲存的指標地址: %x\n", other_ptr )
}
}
4.2 指標陣列
指標陣列:簡單點說它是一個陣列,陣列裡面的每個元素都是指標,陣列佔多少個位元組由陣列本身決定。它是“儲存指標的陣列”的簡稱。格式如下
var ptr [MAX]* type;
ptr 為type指標陣列,因此每個元素都指向了一個值,只是它的值是指標。
package main
import "fmt"
const MAX int = 5
func main() {
var city = [MAX]string{ "北京", "上海","廣州","深圳","濮陽"}
var i int
var othercity [MAX]*string;
for i = 0; i < MAX; i++ {
othercity[i] = &city[i] /* 字串地址賦值給指標陣列 */
}
for i = 0; i < MAX; i++ {
fmt.Printf("指標陣列:索引:%d 值:%s 值的記憶體地址:%d\n", i, *othercity[i] , othercity[i] )
}
}
4.3 指標的指標
如果一個指標變數存放的又是另一個指標變數的地址,則稱這個指標變數為指向指標的指標變數。
當定義一個指向指標的指標變數時,第一個指標存放第二個指標的地址,第二個指標存放變數的地址。
指向指標的指標變數宣告格式如下:
var ptr **type;
表示指向指標的指標變數為type。訪問指向指標的指標變數值需要使用兩個 * 號。
package main
import "fmt"
func main() {
var age int
var ptr *int
var pptr **int
age = 99
/* 指標 ptr 地址 */
ptr = &age
/* 指向指標 ptr 地址 */
pptr = &ptr
/* 獲取 pptr 的值 */
fmt.Printf("變數 age = %d\n", age )
fmt.Printf("指標變數 *ptr = %d,記憶體地址是:%d\n", *ptr, ptr )
fmt.Printf("指向指標的指標變數 **pptr = %d,記憶體地址是:%d\n", *pptr, pptr)
}
4.4 透過new函式建立指標
go支援透過new函式建立指標, new函式將型別作為引數,並返回指向新分配的作為引數傳遞的型別的零值的指標。
package main
import "fmt"
func main() {
//透過new函式建立指標
size := new(int)
fmt.Printf("size的預設值= %d, 型別是: %T, 地址是: %v\n", *size, size, size)
*size = 99
fmt.Println("更改後新的值是:", *size)
}
4.5 指標引數
可以將指標傳遞給函式
package main
import "fmt"
func changeValue(val *int) {
*val = 66
}
func main() {
var age int
var ptr *int
var pptr **int
age = 99
/* 指標 ptr 地址 */
ptr = &age
/* 指向指標 ptr 地址 */
pptr = &ptr
/* 獲取 pptr 的值 */
fmt.Printf("變數 age = %d\n", age )
fmt.Printf("指標變數 *ptr = %d,記憶體地址是:%d\n", *ptr, ptr )
fmt.Printf("指向指標的指標變數 **pptr = %d,記憶體地址是:%d\n", *pptr, pptr)
//函式呼叫
changeValue(ptr)
fmt.Printf("變數age更改後的值 = %d\n", age )
//透過new函式建立指標
size := new(int)
fmt.Printf("size的預設值= %d, 型別是: %T, 地址是: %v\n", *size, size, size)
*size = 99
fmt.Println("更改後新的值是:", *size)
}
特別注意這這兩種傳參的區別
func change(val int) {
val = 88
}
func changeValue(val *int) {
*val = 66
}
函式是基本的程式碼塊,用於執行一個任務。
Go 語言最少有個 main() 函式。
你可以透過函式來劃分不同功能,邏輯上每個函式執行的是指定的任務。
函式宣告告訴了編譯器函式的名稱,引數和返回型別。格式如下
func function_name( [parameter list] ) [return_types] {
//函式體
}
函式定義解析:
- func:函式由關鍵字 func 開始宣告
- function_name:函式名稱,函式名和引數列表一起構成了函式簽名。
- parameter list:引數列表,引數就像一個佔位符,當函式被呼叫時,你可以將值傳遞給引數,這個值被稱為實際引數。引數列表指定的是引數型別、順序、及引數個數。引數是可選的,也就是說函式也可以不包含引數。
- return_types:返回型別,函式返回一列值。return_types 是該列值的資料型別。有些功能不需要返回值,這種情況下 return_types 不是必須的。
- 函式體:函式定義的程式碼集合。
5.1 無參無返回值函式
package main
import (
"fmt"
)
func hello(){
fmt.Println("Hello World")
}
func main() {
fmt.Println("函式開始了")
hello()
}
5.2 有參無返回值函式
package main
import (
"fmt"
)
func hello(message string){
fmt.Println(message)
}
func main() {
fmt.Println("函式開始了")
hello("hello world")
}
5.3 無參有返回值函式
package main
import (
"fmt"
)
func hello() string {
return "Hello world"
}
func main() {
fmt.Println("函式開始了")
greeting := hello()
fmt.Println(greeting)
}
5.4 有參有返回值函式
package main
import (
"fmt"
)
func hello(message string) string {
return message
}
func main() {
fmt.Println("函式開始了")
greeting := hello("hello world")
fmt.Println(greeting)
}
5.5 多個引數多返回值函式
package main
import (
"fmt"
)
func hello(name string, age int) (string, int) {
return name, age
}
func main() {
fmt.Println("函式開始了")
name, age := hello("董廣明", 99)
fmt.Printf("name=%s, age = %d\n", name,age )
}
5.6 在函式中預定義返回值的函式
package main
import (
"fmt"
)
func hello() (message string) {
message = "hello world!"
return
}
func main() {
fmt.Println("函式開始了")
greeting := hello()
fmt.Printf(greeting)
}
message被定義為返回變數。 因此,定義的變數message將自動返回,而無需在最後的return語句中定義。
5.7 捨棄返回值的函式
package main
import (
"fmt"
)
func hello() (string, string) {
return "hello world!", "hahaha"
}
func main() {
fmt.Println("函式開始了")
greeting, _ := hello()
fmt.Printf(greeting)
}
5.8 指標傳遞函式
package main
import (
"fmt"
)
func change(name *string) {
*name = "dongguangming"
}
func main() {
fmt.Println("函式開始了")
name := "董廣明"
fmt.Println(name)
change(&name)
fmt.Printf(name)
}
會改變原來的值,這很容易理解
5.9 可變引數函式
您可以使用Golang中的…運算子來傳遞陣列,也可以使用相同的…運算子來接收引數。
package main
import (
"fmt"
)
func print(items ...string) {
for _, v := range items {
fmt.Println(v)
}
}
func main() {
fmt.Println("函式開始了")
print("董廣明", "dongguangming", "dmg")
list := []string{"Hello", "World"}
print(list...) // An array argument
}
5.10 匿名函式
沒有名字的函式被稱為匿名函式
package main
import (
"fmt"
)
func main() {
fmt.Println("函式開始了")
func () {
fmt.Println("我是一個匿名函式")
} ()
}
golang的世界裡沒有像java一樣有class類的概念,但它有像C語言一樣的結構體Struct。
結構體是不同欄位的型別化集合,結構體用於將資料分組在一起。
例如,如果我們要對User型別的資料進行分組,則定義一個user的屬性,其中可以包括姓名,年齡,性別,所在城市。 可以使用以下語法定義結構
package main
import "fmt"
type User struct {
name string
age int
gender string
city string
}
func main() {
fmt.Println("結構體開始了")
user := User{name: "董廣明", age: 31, gender: "man", city: "南京"}
fmt.Println(user)
//簡寫
u := User{"董廣明", 31, "man", "南京"}
fmt.Println(u)
//透過指標訪問
u1 := &User{"董廣明", 31, "man", "南京"}
fmt.Println(u1.name)
}
6.1 方法
方法是帶有接收器的一種特殊函式。 接收者可以是值或指標。
package main
import "fmt"
type User struct {
name string
age int
gender string
city string
}
// 方法定義
func (u *User) describe() {
fmt.Printf("%v 今年 %v歲了\n", u.name, u.age)
}
//指標引數
func (u *User) setAge(age int) {
u.age = age
}
//值引數
func (u User) setName(name string) {
u.name = name
}
func main() {
fmt.Println("結構體開始了")
user := User{name: "董廣明", age: 31, gender: "man", city: "南京"}
fmt.Println(user)
user.describe()
user.setAge(99)
fmt.Println(user.age)
user.setName("dongguangming")
fmt.Println(user.name)
user.describe()
}
如圖可以看到,現在可以使用點運算子user.describe呼叫該方法。 注意,接收者是一個指標。 使用指標,我們傳遞了對該值的引用,因此,如果對方法進行任何更改,它將反映在接收器user中。它也不會建立該物件的新副本,從而節省了記憶體。
請注意,在上面的示例中,age的值已更改,而name的值未更改,因為方法setName是接收者型別,而setAge是指標型別。
6.2 結構體域欄位匯出
如果欄位名以大寫字母開頭,則定義該結構的包外部的程式碼將可對其進行讀寫。 如果該欄位以小寫字母開頭,則只有該結構包中的程式碼才能讀取和寫入該欄位。
package main
import "fmt"
type User struct {
Name string
Type string
password string
}
func main() {
u := User{
Name: "董廣明",
Type: "1",
password: "secret",
}
fmt.Println(u.Name, " 級別:", u.Type)
fmt.Println("密碼是:", u.password)
}
在同一包中,我們可以訪問這些欄位,如本示例所示, 由於main也在主程式包中,因此它可以引用u.password並檢索儲存在其中的值。 通常在結構體中具有未匯出的欄位,並透過匯出的方法來訪問它們。類比java裡的訪問許可權private域,public訪問方法。
以上程式輸出結果
董廣明 級別: 1
密碼是: secret
6.3 建立匿名結構體
匿名僅建立新的結構變數,而不定義任何新的結構體型別。
package main
import "fmt"
func main() {
emp := struct {
firstName, lastName string
age, salary int
}{
firstName: "董",
lastName: "廣明",
age: 31,
salary: 5000,
}
fmt.Println("員工", emp)
}
以上程式程式碼中,定義了一個匿名結構變數emp, 正如我們已經提到的,該結構稱為匿名結構,因為它僅建立一個新的結構變數emp,而不定義任何新的結構型別。
輸出結果
員工 {董 廣明 31 5000}
6.4 巢狀結構體
結構可能包含一個欄位,而該欄位又是一個結構。 這些型別的結構稱為巢狀結構。
比如我們經常網上購物時,填寫訂單接收地址(家裡,公司,寄存點)
package main
import (
"fmt"
)
type Address struct {
city, state string
}
type Person struct {
name string
age int
address Address
}
func main() {
var p Person
p.name = "董廣明"
p.age = 31
p.address = Address {
city: "南京某街道",
state: "第一收貨地址",
}
fmt.Println("名字:", p.name)
fmt.Println("年齡:",p.age)
fmt.Println("收貨地址:",p.address.city)
fmt.Println("是否第一:",p.address.state)
}
上面程式中的Person結構具有一個欄位地址address,該欄位地址又是一個結構體。
6.5 指向結構體的指標
您可以使用&運算子獲取指向結構體的指標
package main
import "fmt"
type User struct {
name string
age int
gender string
city string
}
func main() {
fmt.Println("結構體開始了")
user := User{name: "董廣明", age: 31, gender: "man", city: "南京"}
fmt.Println(user)
// Pointer to the user struct
user_point := &user
fmt.Println(user_point)
// Accessing struct fields via pointer
fmt.Println((*user_point).name)
fmt.Println(user_point.name) // Same as above: No need to explicitly dereference the pointer
user_point.age = 99
fmt.Println(user_point)
}
以上示例所示,Go使您可以直接透過指標訪問結構體的欄位。
6.6 結構體是值型別
結構體是值型別,當您將一個結構體變數分配給另一個時,將建立並分配該結構的新副本。 同樣,當您將結構體傳遞給另一個函式時,該函式將獲得其自己的結構副本。
package main
import "fmt"
type User struct {
name string
age int
gender string
city string
}
func main() {
fmt.Println("結構體開始了")
user := User{name: "董廣明", age: 31, gender: "man", city: "南京"}
user2 := user // A copy of the struct `user` is assigned to `user2`
fmt.Println("user = ", user)
fmt.Println("user2 = ", user2)
//
user2.name = "dongguangming"
user2.age = 99
user2.city ="上海"
fmt.Println("\n更改user2:")
fmt.Println("user = ", user)
fmt.Println("user2 = ", user2)
}
Go程式設計提供了另一種稱為介面的資料型別,它表示一組方法簽名。
struct資料型別實現介面中定義的方法。
package main
import "fmt"
import "math"
type Shape interface {
area() float64
}
type Rectangle struct{
height float64
width float64
}
type Circle struct{
radius float64
}
func (r Rectangle) area() float64 {
return r.height * r.width
}
func (c Circle) area() float64 {
return math.Pi * math.Pow(c.radius, 2)
}
func getArea(shape Shape) float64{
return shape.area()
}
func main() {
fmt.Println("介面開始了")
rect := Rectangle{20, 50}
circ := Circle{4}
fmt.Println("長方形面積 =", getArea(rect))
fmt.Println("圓的面積 =", getArea(circ))
}
通道是一種型別化的管道,可以使用通道運算子<-傳送和接收值。
channel<- value // 傳送value值到通道channel
value := <-channel // 從通道查詢,並把值賦給value
注意:資料按箭頭方向流動
和其他資料型別類似,通道使用前必須先建立,其初始值是 nil。建立通道的語法格式如下:
var c1 chan [value type]
c1 = make([channel type] [value type], [capacity])
- [value type] 定義的是 Channel 中所傳輸資料的型別。
- [channel type] 定義的是 Channel 的型別,其型別有以下三種:
- “chan” 可讀可寫——“chan int” 則表示可讀寫 int 資料的 channel
- “chan<-“ 僅可寫——“chan<- float64” 則表示僅可寫64位 float 資料的 channel
- “<-chan” 僅可讀——“<-chan int” 則表示僅可讀 int 資料的 channel
- [capacity] 是一個可選引數,其定義的是 channel 中的快取區 (buffer)。如果不填則預設該 channel 沒有緩衝區 (unbuffered)。對於沒有緩衝區的 channel,訊息的傳送和收取必須能同時完成,否則會造成阻塞並提示死鎖錯誤。
比如我們想建立了一個讀寫 int 型別,buffer 長度 100 的 channel c1,則如下:
var c1 chan int
c1 = make(chan int, 100)
透過此通道,我們可以傳送int型別的資料。 我們可以在此通道中傳送和接收資料
package main
import "fmt"
func main() {
ch := make(chan int)
go func() { ch <- 31 }()
age := <-ch
fmt.Println(age)
}
接收方通道等待,直到傳送方將資料傳送到通道。
8.1 單向通道
在有些情況下,我們希望透過通道接收資料但不傳送資料, 為此我們還可以建立一個單向通道。 讓我們看一個簡單的例子:
package main
import (
"fmt"
)
func main() {
ch := make(chan string)
go sc(ch)
fmt.Println(<-ch)
}
func sc(ch chan<- string) {
ch <- "你好,董廣明"
}
在上面的示例程式碼中,sc是go協程,該例程只能將訊息傳送到通道,但不能接收訊息。
執行程式碼輸出結果
$go run main.go
你好,董廣明
8.2 快取通道(Buffered channel)
在Golang中可以建立一個緩衝通道。 對於緩衝的通道,如果緩衝區已滿,則將阻止傳送到該通道的訊息。 讓我們看一個小例子
package main
import "fmt"
func main() {
ch := make(chan string, 2)
ch <- "hello"
ch <- "world"
ch <- "!!!" // 超了緩衝最大值要報錯
fmt.Println(<-ch)
}
實際上超過緩衝最大值,要報錯
那怎麼辦呢,還好有以下解決方法
package main
import "fmt"
func main() {
ch := make(chan string, 2)
ch <- "hello"
ch <- "world"
//建立匿名函式
function := func(name string) { ch <- name }
//
go function("董廣明")
go function("donguangming")
go function("dgm")
go function("dgmdgm")
go function("3dgm")
fmt.Println(<-ch)
fmt.Println(<-ch)
fmt.Println(<-ch)
fmt.Println(<-ch)
fmt.Println(<-ch)
fmt.Println(<-ch)
fmt.Println(<-ch)
}
Golang Tutorial — from zero to hero milapneupane.com.np/2019/07/06/lea...
Understanding Maps in Go www.digitalocean.com/community/tut...
Golang Maps www.geeksforgeeks.org/golang-maps/
Golang Tutorial – Learn Golang by Examples www.edureka.co/blog/golang-tutoria...
The anatomy of Slices in Go medium.com/rungo/the-anatomy-of-sl...
GoLang Tutorial - Structs and receiver methods - 2020 www.bogotobogo.com/GoLang/GoLang_S...
Golang Cheatsheet: Functions ado.xyz/blog/golang-cheatsheet-fun...
Ultimate Guide to Go Variadic Functions medium.com/m/global-identity?redir...
Golang Methods Tutorial with Examples www.callicoder.com/golang-methods-...
Go Best Practices: Should you use a method or a function? flaviocopes.com/golang-methods-or-...
Methods that satisfy interfaces in golang suraj.io/post/golang-methods-inter...
Pass by pointer vs pass by value in Go goinbigdata.com/golang-pass-by-poi...
Go Data Structures: Interfaces research.swtch.com/interfaces
How to Define and Implement a Go Interface code.tutsplus.com/tutorials/how-to...
Methods and Interfaces in Go dev-pages.info/golang-interfaces/
Go (Golang) - understanding the object oriented features with structs, methods, and interfaces unixsheikh.com/articles/go-underst...
理解 Golang 的 Channel 型別 studygolang.com/articles/25805
Anatomy of Channels in Go - Concurrency in Go medium.com/rungo/anatomy-of-channe...
本作品採用《CC 協議》,轉載必須註明作者和本文連結