Go结构体
结构体是将多个容易类型的命令变量组合在一起的聚合数据类型。
每个变量都成为该结构体的成员变量。
可以理解为Go语言的结构体struct和其他语言的class有相等的地位,但是Go语言放弃大量面向对象的特性,所有的Go语言类型除了指针类型外,都可以有自己的方法,提高了可扩展性。
案例:
package main
import "fmt"
type Person struct{
Name string
Age int
}
func main(){
var p1 Person
p1.Name = "Yven"
p1.age = 19
fmt.Println(p1.Name,p2.age)
}
成员变量
访问控制机制
如果一个结构体的成员变量名称是首字母大写的,那么这个变量是可导出的。(即在其他包可以访问到)
一个结构体可以同时包含可导出和不可导出的成员变量。
type Person struct {
Name string //不可导出
age int // 可导出
}
限制
命名结构体类型s不可以定义一个拥有相同结构体类型s的成员变量,也就是一个聚合类型不可以包含它自己。但是s中可以定义一个s的指针类型,即*s。
type Person struct {
Name string
p1 Person //错误
p2 *person //正确
}
结构内嵌(匿名字段)
Go语言中没有像java,python中继承的概念,不过他有一个类似的功能,结构内嵌。
案例:
package main
import "fmt"
type Person struct{
Name string
Age int
}
type Student struct{
Person
Class string
}
func main(){
var stu Student
stu.Name = "Yven"
stu.Age = 20
stu.Class = "计算机152"
fmt.Println(stu.Name, stu.Age, stu.Class)
}
如果嵌入结构的字段和外部结构的字段相同,那么,想要修改嵌入结构的字段值需要加上外部结构中声明的嵌入结构名称。
案例:
package main
import "fmt"
type Person struct{
Name string
Age int
}
type Student struct{
Person
Class string
Name string
}
func main(){
var stu Student
stu.Name = "Yven"
stu.Person.Name = "Yven_law"
stu.Age = 20
stu.Class = "计算机152"
}
Go语言中没有构造方法,但是可以通过工厂模式来解决这个问题,案例如下:
package main
import "fmt"
type Person struct{
Name string
Age int
}
func NewPerson(name string,age int) *Person{
person := &Person{name,age}
return person
}
func main(){
p := NewPerson("Yven", 18)
fmt.Println(p.name, p.age) //输出 Yven 18
}
方法
Go中除了指针和interface以外的所有类型都可以有方法,不仅仅是struct
方法的定义方式:
func (r ReceiverType) funcName(parameters)(results)
简单案例验证所有类型都可以拥有方法:
package main
import "fmt"
type Int int //没办法直接使用int来进行验证,不过可以对int进行封装后来测试
func (a Int) compare(b Int) bool}{
return a < b
}
func main(){
a := 2
b := 5
fmt.Println(a.compare(b))
}
方法的声明和普通函数的声明类似,知识在函数名字前面多加了一个参数,这个参数把这个方法绑定在该参数对应的类型上。该参数成为方法的接受者。
案例:
type Person struct{
Name string
Age int
}
func (p Person) eat(n string){
fmt.Printf("%v 吃了 %v",p.Name,n)
}
指针接受者的方法
由于主调函数会复制每一个实参变量,或者如果一个实参太大而我们希望避免复制整个实参,因此我们必须使用指针来传递变量的地址。这也同样适用于更新接收者我们将它绑定到指针类型。在调用方法的时候,编译器会对变量进行隐式转换。
总结一下结构体方法可以成功调用的条件:
- 实参接收者和形参接收者是同一类型,比如都是T或者都是*T。(1,4,5,7)
- 实参接收者是T类型的变量而形参接收者是*T类型,编译器会隐式的获取变量的地址(3)。
- 实参接收者是T类型而形参接收者是T类型,编译器会隐式的获取实际的取值。(2,6)其中8编译过程报错的原因是:编译器对T类型转化为T类型的隐式转化,只有实参接收者是变量才可以成功,因为无法获取临时变量的地址。
type Point struct {
X int
Y int
}
func (p Point) Print() {
fmt.Println(p.X, p.Y)
}
func (p *Point) ScaleBy(factor int) {
p.X *= factor
p.Y *= factor
}
func main() {
p := Point{1,1}
ptr := &p
p.Print() //1. 正确
ptr.Print() //2. 正确
p.ScaleBy(2) //3. 正确
ptr.ScaleBy(2) //4. 正确
Point{1,1}.Print() //5. 正确
(&Point{1,1}).Print() //6. 正确
(&Point{1,1}).ScaleBy( 2) //7. 正确
Point{1,1}.ScaleBy( 2) //8. 错误
}