Go沒有類似傳統面向對象的構造函數,但是我們可以通過結構體來使用構造函數初始化結構體類型。
我們在使用構造函數要注意struct是值類型如果結構體比較複雜最好返回結構體指針類型,因為值拷貝性能開銷大。
package main
import "fmt"
type Server struct {
address string
port int
}
func NewServer() *Server {
return &Server{
address: "localhost",
port: 8080,
}
}
func main() {
server1 := NewServer()
fmt.Println(server1)
}
// result &{localhost 8080}
Go中的工廠模式其實就是包内一個不可直接實例的結構體(結構體名稱首字母小寫即私有結構體),包外不可直接實例化實例,那麼為了解決這個問題可以寫一個包外可調用的函數,通過這個函數實現返回結構體對象。
// main.go 代碼
package main
import (
"fmt"
"app/model"
)
func main(){
p1 := model.NewPerson("jack", 20)
age := p.GetAge()
fmt.Println(*p1)
fmt.Println(age)
}
// result
// {jack 20}
// 20
// app/model.go
package model
type person struct(
Name string
age int
)
func NewPerson(name string,age int) *person{
return &person{
Name: name,
age: age,
}
}
func (p *person) GetAge() int{
return p.age
}
類型别名是Go在1.9版本之後添加的新特性,主要是用在代碼升級、代碼重構和遷移中類型的兼容性。
type byte uint8
type rune int32
類型别名指定類型别名隻是類型的别名。本質上類型的别名和類型是相同的類型,即基本數據類型是相同的。
package main
import "fmt"
type myInt int
type intAlias = int
func main() {
var a myInt
fmt.Printf("a Type: %T, value: %d\n", a, a)
var b intAlias
fmt.Printf("b Type: %T, value: %d\n", b, b)
}
// result
// a Type: main.myInt, value: 0
// b Type: int, value: 0
通過上面的代碼來看,在表面上類型别名和自定義類型之間隻有相等的符号差異(“ =”),實際上我們可以代碼運行結構看到a的類型是myint而且是main包定義的myInt類型,即生成了新的數據類型,而b的類型是int,則表示intAlias類型僅在代碼中體現而編譯完成之後沒有intAlias類型。
結構體中的方法及接受者Go 語言支持方法,Go方法類似于 Go 函數,但有一個區别即該方法包含一個接收者參數。通過接受者參數該方法就可以訪問接收者的屬性。這裡接收者表示可以是結構類型或非結構類型,在代碼中創建方法時,接收者和接收者類型必須存在于同一個包中。并且不允許創建接收者類型已在另一個包中定義的方法,包括 int、string 等内置類型。如果這樣做,Go編譯器将給出錯誤。
func (接收者變量 接收者類型) 方法名(參數列表) (返回參數) {
函數體
}
使用值類型接受者的方法時無法改變接受者變量的值。
package main
import "fmt"
type person struct {
name, job string
age int
}
func (p person) show() {
fmt.Println("Person's Name: ", p.name)
fmt.Println("Job: ", p.job)
fmt.Println("age:", p.age)
}
func main() {
res := person{
name: "jack",
job: "engine",
age: 20,
}
res.show()
}
// result
// Person's Name: jack
// Job: engine
// age: 20
結構體指針類型的接收者由一個結構體的指針組成,由于指針的特性,調用方法時修改接收者指針的任意成員變量,在方法結束後,修改都是有效的。
package main
import "fmt"
type user struct {
name, job string
age int
}
func (u *user) SetAge(age int) {
u.age = age
}
func main() {
res := user{
name: "jack",
job: "engine",
age: 19,
}
fmt.Println("user's name: ", res.name)
fmt.Println("user's job: ", res.job)
fmt.Println("user's age: ", res.age)
u := &res
u.SetAge(22)
fmt.Println("user's name: ", res.name)
fmt.Println("user's job: ", res.job)
fmt.Println("user's age: ", res.age)
}
// result
// user's name: jack
// user's job: engine
// user's age: 19
// user's name: jack
// user's job: engine
// user's age: 22
在 Go 語言中,隻要類型和方法定義存在于同一個包中,就可以創建具有非結構類型接收器的方法。如果不存在同一個包裡,那麼編譯器就會報錯,因為它們是在不同的包中定義的。
package main
import "fmt"
type data int
func (d1 data) multiply(d2 data) data {
return d1 * d2
}
func main() {
value1 := data(20)
value2 := data(20)
res := value1.multiply(value2)
fmt.Println("Final result: ", res)
}
// result
// Final result: 400
重點:什麼時候使用值類型接受者方法,什麼時候使用指針類型接受者方法。
結構體嵌套可以模拟面向對象編程繼承的特性中以下兩種關系:
聚合關系:一個類作為另一個類的屬性,聚合關系一定不能是匿名結構體必須用有名字的結構體作為結構體字段
繼承關系:一個類作為另一個類的子類。子類和父類的關系。匿名結構體字段的形式就是繼承關系
package main
import "fmt"
type Address struct {
province, city string
}
type Person struct {
name string
age int
address *Address
}
func main() {
p1 := Person{name: "jack", age: 19, address: &Address{province: "Guangdong", city: "Guangzhou"}}
fmt.Printf("Name: %s\nAge: %d\nProvince: %s\nCity: %s\n", p1.name, p1.age, p1.address.province, p1.address.city)
// 當你修改Person結構體實例化p1的數據時,結構體Address的數據也會發生變化
p1.address.province = "Hunan"
p1.address.city = "Changsha"
fmt.Printf("Province: %s\nCity:%s\n", p1.address.province, p1.address.city)
}
如果一個結構體嵌套了另一個匿名結構體,那麼這個結構體可以直接訪問匿名結構體的方法,從而實現繼承。
package main
import "fmt"
type Cat struct {
age int
name string
}
func (c *Cat) Run() {
fmt.Println("Cat Running")
}
type WhiteCat struct {
Cat
color string
}
type BlackCat struct {
Cat
}
func (p *BlackCat) String() string {
str := fmt.Sprintf("name=[%s] age=[%d]", p.name, p.age)
return str
}
func main() {
var a WhiteCat
a.age = 2
a.name = "tom"
a.color = "white"
fmt.Println(a)
a.Run()
var b BlackCat
b.age = 3
b.name = "rise"
b.Run()
fmt.Printf("%s", &b)
}
注意
結構體嵌套時可能存在相同的屬性名,屬性名重名會導緻屬性名沖突,盡量避免這種情況。
結構體字段的私有屬性和公開屬性結構體中字段大寫開頭表示可公開訪問的屬性,小寫表示私有屬性(僅在定義當前結構體的包中可訪問)。
結構體标簽(Tag)和JSON序列化結構體tag是結構體的元信息,可以在運行的時候通過反射的機制讀取出來。 Tag在結構體字段的後方定義,由一對反引号包裹起來。
結構體标簽由一個或多個鍵值對組成。鍵與值使用冒号分隔,值用雙引号括起來。鍵值對之間使用一個空格分隔。
type user struct {
Name string `json:name` // 通過指定tag實現json序列化該字段時的key
password string // 私有屬性不能被json訪問
}
package main
import (
"encoding/json"
"fmt"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
func main() {
u1 := User{
ID: 1,
Name: "jack",
}
data, err := json.Marshal(u1)
if err != nil {
fmt.Println("json marshal failed!")
return
}
fmt.Printf("json str:%s\n", data)
}
package main
import (
"encoding/json"
"fmt"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
var r User
s := `{
"name":"jack",
"age":18,
"id":1
}`
err := json.Unmarshal([]byte(s), &r)
if err != nil {
fmt.Print(err)
}
fmt.Printf("結構體轉換為字符串之後的值為:%#v", r)
}
更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!