GO开发[五]:结构体struct和方法
Go结构体struct
Go语言的结构体(struct)和其他语言的类(class)有同等的地位,但Go语言放弃了包括继承在内的大量面向对象特性,只保留了组合(composition)这个最基础的特性。
package main
import (
"fmt"
)
//声明
type Student struct {
Id int
Name string
age int
}
func main() {
var s Student
s.Id =1
s.Name ="greg"
s.age=20
fmt.Println(s)
fmt.Printf("Name:%p\n", &s.Name)
fmt.Printf("Id:%p\n", &s.Id)
fmt.Printf("Age: %p\n", &s.age)
s1 := Student {
Id :2,
Name:"gregory",
age:20,
}
fmt.Println(s1)
s1=s
fmt.Println(s1)
var s2 *Student = &Student{
Id:3,
age: 20,
Name: "greg",
}
fmt.Println(s2)
}
结构体指针
package main
import "fmt"
//结构体指针
type Student struct {
Id int
Name string
}
func main(){
s1 := Student{
Id :2,
Name:"greg",
}
fmt.Println(s1)
var p *Student
p = &s1
p.Id =3
fmt.Println(s1)
var p1 *int
p1 = &s1.Id
*p1 = 4
fmt.Println(s1)
}
结构体性质
结构体是用户单独定义的类型,不能和其他类型进行强制转换
package main
import "fmt"
type integer int
type Student struct {
Id int
}
type Stu Student //alias
func main() {
var i integer = 1000
var j int = 100
//j = i cannot use
j = int(i)
fmt.Println(j)
var a Student
a = Student{30}
fmt.Println(a)
var b Stu
//a=b cannot use
a = Student(b) //转换
fmt.Println(a)
}
我们可以为struct中的每个字段,写上一个tag。这个tag可以通过反射的机制获取到,最常用的场景就是json序列化和反序列化。
type student struct {
Id int “this is Id field”
Name stirng “this is name field”
}
序列化和反序列化
package main
import (
"fmt"
"encoding/json"
"log"
)
type Student struct {
Id int
Name string
}
func main(){
s := Student{
Id:1,
Name:"greg",
}
fmt.Println(s)
buf,err := json.Marshal(s)
if err != nil{
log.Fatalf("marshal error:%s",err)
}
fmt.Println(string(buf))
str := `{"Id":2,"Name":"alice"}`
var s2 Student
err1 := json.Unmarshal([]byte(str),&s2)
if err1 != nil {
log.Fatalf("unmarshal error:%s",err)
}
fmt.Println(s2)
}
//将Student map的数据写入到文件
结构体中字段可以没有名字,即匿名字段
访问匿名字段
package main
import (
"fmt"
"time"
)
type Student struct {
name string
age int
}
type Class struct {
Student
name string
int
Start time.Time
age int
}
type School struct {
Student
Class
}
func main() {
var t Class
t.Student.name = "stu"
t.Student.age = 100
t.name="cls"
t.age=80
t.int=2000
fmt.Println(t)
}
匿名字段冲突处理
package main
import (
"fmt"
)
type Student struct {
name string
age int
}
type Class struct {
name string
age int
}
type School struct {
Student
Class
}
func main() {
var t School
t.Student.name = "stu"
t.Class.name = "cls"
fmt.Println(t)
}
方法
Golang中的方法是作用在特定类型的变量上,因此自定义类型,都可以有方法,而不仅仅是struct
定义:func (recevier type) methodName(参数列表)(返回值列表){}
方法调用
package main
type Point struct {
X, Y float64
}
func (p *Point) ScaleBy(factor float64) {
p.X *= factor
p.Y *= factor
}
func main() {
//直接指针
p := &Point{1,2}
p.ScaleBy(2)
//声明结构体后再用指针指向
p1 := Point{1,2}
p2 := &p1
p2.ScaleBy(2)
//使用结构体调用,隐式取地址
p3 := Point{1,2}
p3.ScaleBy(2) //等价于(&p3).ScaleBy(2)
}
结构体方法调用:
package main
import "fmt"
type Student struct {
Id int
Name string
Age int
Score int
}
func (p *Student) init(name string, age int, score int) {
p.Name = name
p.Age = age
p.Score = score
fmt.Println(p)
}
func (p Student) get() Student {
return p
}
func main() {
var stu Student
(&stu).init("stu", 10, 200)
stu1 := stu.get()
fmt.Println(stu1)
}
练习:两点之间的距离
package main
import (
"fmt"
"math"
)
type Point struct {
X,Y float64
}
func (p Point) Distance(q Point) float64 {
return math.Hypot(q.X-p.X, q.Y-p.Y)
}
func main() {
p := Point{1,2}
q := Point{4,6}
fmt.Println(p.Distance(q))
}
方法和函数的区别:
函数调用: function(variable, 参数列表)
方法:variable.function(参数列表)
指针receiver和值receiver:本质上和函数的值传递和地址传递是一样的
方法的访问控制:可见性
通过首字母大小写来控制可见性
可见性是package级别的
继承:
如果一个struct嵌套了另一个匿名结构体,那么这个结构可以直接访问匿名结构体的方法,从而实现了继承。
package main
import "fmt"
type Student struct {
Id int
name string
}
func (p *Student) Run() {
fmt.Println("greg")
}
type Class struct {
Student
golang string
}
type School struct {
gregs Student
}
func main() {
var c Class
c.Id = 100
c.name = "greg2"
c.golang = "一班"
fmt.Println(c)
c.Run()
var sch School
sch.gregs.Id = 100
sch.gregs.name = "train"
fmt.Println(sch)
sch.gregs.Run()
}
组合和匿名字段:
如果一个struct嵌套了另一个匿名结构体,那么这个结构可以直接访问匿名结构体的方法,从而实现了继承。
如果一个struct嵌套了另一个有名结构体,那么这个模式就叫组合。
多重继承:如果一个struct嵌套了多个匿名结构体,那么这个结构可以直接访问多个匿名结构体的方法,从而实现了多重继承。