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嵌套了多个匿名结构体,那么这个结构可以直接访问多个匿名结构体的方法,从而实现了多重继承。