结构体变量不仅仅时拥有自己的属性,而且还拥有自己的方法,比如一个人不仅仅拥有姓名、年龄,他还有一些行为,这些行为在结构体变量中就可以理解为方法。Golang中的方法是与具体的数据类型进行绑定的,因此也不仅仅时结构体(struct)拥有自己的方法。
func(receiver type) methodName(参数列表) (返回值列表){ 方法体 return 返回值 }
比如:
package main import "fmt" type User struct { Name string Age int } func (user User) getUser() { fmt.Println(user.Name, user.Age) } func main() { var user User user.Name = "bily" user.getUser() }
package main import "fmt" type CalSum struct { n1 int n2 int } // 结构体调用方法,该方法可以有入参参数、返回值 func (cal CalSum) getSum() int { res := cal.n1 + cal.n2 return res } func main() { // 生成一个结构体变量 cal := CalSum{ 1, 2, } // 调用方法 res := cal.getSum() fmt.Println(res) }
方法与函数类似,但是最大的不同就是方法会将调用者当作参数传递给方法,比如上图中会将Cal当作参数传递给getSum方法,并且需要注意的是如果是值类型则进行值拷贝,如果是指针类型则进行引用传递,这与形参类型有关。
默认的就是值类型传递,这也就意味着方法中结构体变量是拷贝main函数中的,所以效率较低,为了提高效率可以进行引用传递。
package main import "fmt" type CalSum struct { n1 int n2 int } // 结构体调用方法,该方法可以有入参参数、返回值 func (cal *CalSum) getSum() int { res := cal.n1 + cal.n2 return res } func main() { // 生成一个结构体变量 cal := CalSum{ 1, 2, } /* 调用方法, 因为方法的形参是指针类型,所以应该是结构体变量地址调用 */ res := (&cal).getSum() // .的运算优先级高,所以(&cal)需要带括号 fmt.Println(res) // 3 /* 但是编译器在底层进行了优化,所以(&cal).getSum() 可以写成cal.getSum() */ res1 := cal.getSum() fmt.Println(res1) /* 此时main中的结构体变量与方法中传入的结构体变量相同,内存中存在形式也与值传递不同 */ }
方法不是struct类型独有的,比如int、float系列类型的都可以拥有方法:
package main import "fmt" type integer int // integer类型方法 func (i *integer) change() { *i = *i + 5 } func main() { var i integer = 5 i.change() fmt.Println(i) // 10 }
如果现在有一个model包,里面有一个结构体变量:
package model // 定义一个结构体 type User struct { Name string Age int }
如果在其它包,比如main包中去创建User结构体实例,这时没问题的,因为User结构体首字母是大写的,但是如果User是小写首字母这就是私有变量这能在本包访问,如果解决跨包访问私有变量的问题呢?这就需要使用工厂模式,简单的说就是定一个公有方法对外提供接口的方式。
package model // 定义一个结构体 type User struct { Name string Age int }
package main import ( "fmt" "go_tutorial/day12/factorMode/model" ) func main() { user := model.User{Name: "lily", Age: 20} fmt.Println(user) }
├─factorMode ├─main │ main.go │ └─model user.go
上面User.go中的结构体变量是大写首字母是没问题,但是如果小写就会出现私有变量无法跨包问题,此时使用工厂模式。
package model // 定义一个私有结体 type user struct { Name string Age int } // 工厂方法,它包可以调用该方法 func NewUser(name string, age int) *user { return &user{ Name: name, Age: age, } }
package main import ( "fmt" "go_tutorial/day12/factoryMode01/model" ) func main() { user := model.NewUser("lily", 20) fmt.Println(*user) // {lily 20} fmt.Println(user.Name, user.Age) // lily 20 }
├─factorMode ├─main │ main.go │ └─model user.go
上述是私有结构体变量跨包问题,但是如果是结构体中的字段首字母小写私有变量,那又如何解决呢?
... // 私有字段name工厂方法 func GetName(u *user) string { return u.name } ...
... func main() { user := model.NewUser("lily", 20) fmt.Println(model.GetName(user)) //lily }
创建一个Box结构体,在其中:
package main import "fmt" type Cell struct { length float64 width float64 height float64 } func (cell *Cell) GetVolume() float64 { return cell.length * cell.width * cell.height } func main() { // 创建一个立方体结构体变量 var cell Cell // 为每个字段赋值 fmt.Print("请输入立方体的长:") fmt.Scanln(&cell.length) fmt.Print("请输入立方体的宽:") fmt.Scanln(&cell.width) fmt.Print("请输入立方体的高:") fmt.Scanln(&cell.height) // 调用结构体变量的方法 res := cell.GetVolume() fmt.Println(res) }