18、Golang 教程 - 结构体和方法

1. 别名定义

变量别名定义

// 有些变量太长,简化变量名
// 为了安全,隐藏原始变量名
package main

import "fmt"

// int 类型起了别名为 integer
type integer int

func main() {
   
     
    // 类型别名定义
    var i integer = 1000
    fmt.Println(i)
    
    var j int = 100
    // 属性不同类型,需要做强转
    j = int(i)
    fmt.Println(j)
}

// 结果
1000
1000

结构体别名定义

package main

import "fmt"

type Student struct {
   
     
    Number int
}

// 结构体别名
type Stu Student

func main() {
   
     
    var a Student
    a = Student{
   
     30}
    
    var b Stu
    b = Stu{
   
     300}
    
    // 强转类型
    a = Student(b)
    fmt.Println(a)
}

// 结果
{
   
     300}

2. 工厂模式

创建构造体时初始化构造体,即对构造体属性变量赋初始值(初始化赋值)。在 Go 中没有构造函数,可以使用工厂模式来解决。

package main

import "fmt"

type Student struct {
   
     
    Name string
    Age int
}

func main() {
   
     
    stu1 := new(Student)
    stu2 := NewStudent("zhangsan",20)
    
    fmt.Println(stu1)
    fmt.Println(stu2)
}

// 工厂模式
func NewStudent(name string, age int) *Student {
   
     
    return &Student {
   
     
        Name: name,
        Age: age,
    }
}

&{
   
      0}
&{
   
     zhangsan 20}

package main

import "fmt"

// 造车图纸
type Car struct {
   
     
    Name string
    Color string
}

func main() {
   
     
    var car1  = new(Car)
    fmt.Println(car1)
    
    var car2 = NewCar("tom","yellow")
    fmt.Println(car2)
}

// 工厂模式
func NewCar(name string, color string) *Car {
   
     
    return &Car {
   
     
        Name: name,
        Color: color,
    }
}

&{
   
      }
&{
   
     tom yellow}

强调:

  • make 用来创建 map、slice、channel
  • new 用来创建值类型

3. Tag 原信息

跟其他语言进行对接交互时使用 JSON 格式,有些语言格式有规范大小写严格,为了使 Go 能和其他语言对接数据传输,故使用 Tag 原信息来解决。

package main

import (
    "fmt"
    "encoding/json"
)

type Student struct {
   
     
    Name string
    Age int
    Score float32
}

func main() {
   
     
    // 初始化:var stu = new(Student) 也可
    var stu Student = Student {
   
     
        Name: "stu1",
        Age: 18,
        Score: 80,
    }
    // 使用 json 格式返回字节数组
    data,err := json.Marshal(stu)
    if err != nil {
   
     
        fmt.Println("json encode stu failed,err:",err)
        return
    }
    fmt.Println(data)	// 字节数组形式输出
    fmt.Println(string(data))	// 转换成字符串输出
}

[123 34 78 97 109 101 34 58 34 115 116 117 49 34 44 34 65 103 101 34 58 49 56 44 34 83 99 111 114 101 34 58 56 48 125]
{
   
     "Name":"stu1","Age":18,"Score":80}

JSON 格式字段名

package main

import (
    "fmt"
    "encoding/json"
)

type Student struct {
   
     
    // json 打包时字段名
    Name string json:"stu_name"
    Age int json:"stu_age"
    Score float32 json:"stu_score"
}

func main() {
   
     
     var stu Student = Student {
   
     
        Name: "stu1",
        Age: 18,
        Score: 80,
    }
    // 使用 json 格式返回字节数组
    // 使用 json 格式返回字节数组
    data,err := json.Marshal(stu)
    if err != nil {
   
     
        fmt.Println("json encode stu failed,err:",err)
        return
    }
    fmt.Println(data)	// 字节数组形式输出
    fmt.Println(string(data))	// 转换成字符串输出
}
[123 34 115 116 117 95 110 97 109 101 34 58 34 115 116 117 49 34 44 34 115 116 117 95 97 103 101 34 58 49 56 44 34 115 116 117 95 115 99 111 114 101 34 58 56 48 125]
{
   
     "stu_name":"stu1","stu_age":18,"stu_score":80}

4. 匿名字段

结构体中的字段(属性)没有名称,称之为匿名字段。

package main

import (
    "fmt"
)

type Cart struct {
   
     
    name string
    color string
}

type Train struct {
   
     
    // 匿名字段,实现继承关系,省略组合形式(c Cart --> Cart)
    // int 是类型,默认情况下,它的数据类型就是它的字段名称
    // java 里只有单继承,想要多继承功能需要定义成接口
    Cart	// 实现继承,单继承一个父类,多继承 N 个父类
    int		// 数据类型定义,仅能存在一次,两个 int 则冲突
}

func main() {
   
     
    var t Train
    t.name = "train"
    t.color = "blue"
    t.int = 20	// 直接调用数据类型赋值
    fmt.Println(t)
}
{
   
     {
   
     train blue} 20}

继承的两个结构体中定义相同属性

package main

import (
    "fmt"
)

type Cart struct {
   
     
    name string
    color string
}

type Box struct {
   
     
    color string
}

type Train struct {
   
     
    Cart
    Box
    int
}

func main() {
   
     
    var t Train
    t.name = "train"
    t.Cart.color = "blue"
    t.Box.color = "yellow"
    t.int = 20
    fmt.Println(t)
}
{
   
     {
   
     train blue} {
   
     yellow} 20}
  Cart		  Box     self

自身定义和继承的结构体相同的属性

package main

import (
    "fmt"
)

type Cart struct {
   
     
    name string
    color string
}

type Train struct {
   
     
    Cart
    int
    color string
}

func main() {
   
     
    var t Train
    t.name = "train"
    t.Cart.color = "blue"
	t.color = "green"
    t.int = 20
    fmt.Println(t)
}
{
   
     {
   
     train blue} 20 green}

双继承

package main

import (
	"fmt"
)

type Cart1 struct {
   
     
	name string
	color string
}

type Cart2 struct {
   
     
	name string
	color string
}

type Train struct {
   
     
	Cart1
	Cart2
}

func main() {
   
     
	var t Train
	t.Cart1.name = "train1"
	t.Cart1.color = "blue"
	t.Cart2.name = "train2"
	t.Cart2.color = "yellow"
	fmt.Println(t)
}
{
   
     {
   
     train1 blue} {
   
     train2 yellow}}
Cart1		   Cart2

5. 方法

Go中的方法是作用在特定类型的变量上,因此自定义类型,都可以有方法,而不仅仅是 struct。

语法

func (recevier type) methodName(参数列表)(返回值){
   
     }

示例

package main

import "fmt"

type Student struct {
   
     
    Name string
    Age int
}

// 方法
// s 归属于 Student
// 传参和返回值可写可不写
func (s Student) init(name string, age int) Student {
   
     
    s.Name = name
    s.Age = age
    return s
}

func main() {
   
     
    var stu Student
    s := stu.init("zhangsan",18)
    fmt.Println(s)
}

// 结果
{
   
     zhangsan 18}

package main

import "fmt"

type Student struct {
   
     
	Name string
	Age int
}

// 方法
func (s Student) chushi(name string, age int) {
   
     
	s.Name = name
	s.Age = age
}

func main() {
   
     
	var stu Student = Student{
   
     
		Name: "lisi",
		Age: 20,
	}
	stu.chushi("zhangsan",18)
	fmt.Println(stu)
}

// 结果
// struct 是值类型,函数体内的修改对外部不影响
{
   
     lisi 20}

package main

import "fmt"

type Student struct {
   
     
	Name string
	Age int
}

// 方法
// 传递指针
func (s *Student) chushi(name string, age int) {
   
     
	s.Name = name
	s.Age = age
}

func main() {
   
     
	var stu Student = Student{
   
     
		Name: "lisi",
		Age: 20,
	}
    // (&stu).chushi("zhangsan",18)	指针自动转化
	stu.chushi("zhangsan",18)
	fmt.Println(stu)
}

// 结果
{
   
     zhangsan 18}

思考:定义返回方法是否会把初始化的值给返回

值传递

package main

import "fmt"

type Student struct {
   
     
	Name string
	Age int
}

// 初始化方法
func (s Student) init(name string, age int) {
   
     
	s.Name = name
	s.Age = age
	fmt.Println("初始化完成")
	fmt.Println(s)
}

// 返回结构体
func (s Student) get() Student {
   
     
	return s
}

func main() {
   
     
	var stu Student
	stu.init("zhangsan",18)
	stu1 := stu.get()
	fmt.Println(stu1)
}

// 结果
初始化完成
{
   
     zhangsan 18}
{
   
      0}

引用传递:要想实现封装的效果,需要使用传递指针

package main

import "fmt"

type Student struct {
   
     
	Name string
	Age int
}

// 初始化方法(类似于封装)
func (s *Student) init(name string, age int) {
   
     
	s.Name = name
	s.Age = age
	fmt.Println("初始化完成")
	fmt.Println(s)
}

// 返回结构体
// get() 是自己定义的,用来读取私有属性的内容
func (s *Student) get() Student {
   
     
	return *s
}

func main() {
   
     
	var stu Student
	stu.init("zhangsan",18)
	stu1 := stu.get()
	fmt.Println(stu1)
}

// 结果
初始化完成
&{
   
     zhangsan 18}
{
   
     zhangsan 18}

传统数据类型自定义方法,做数据类型转化

// 方法可以绑定传统类型,不一定是结构体
package main

import "fmt"

type Integer int

// 传统数据类型自定义方法
func (i Integer) convert() string {
   
     
    return fmt.Sprintf("%d",i)
}

// 方法:数值类型转换成字符串
func main() {
   
     
    var i Integer
    i = 100
    s := i.convert()
    fmt.Printf("类型:%T\n值:%s\n",s,s)
}

// 结果
类型:string
值:100

指针传入和值传入的区别

package main

import "fmt"

type Integer int

// 值传递
func (i Integer) convert() string {
   
     
    return fmt.Sprintf("%d",i)
}

// 指针传递
func (p *Integer) set(b Integer) {
   
     
    *p = b
}

func main() {
   
     
    var i Integer
    i = 100
    s := i.convert()
    fmt.Printf("类型:%T,值:%s\n",s,s)
    fmt.Printf("类型:%T,值:%d\n",i,i)
    i.set(200)
    fmt.Println(i)
}

// 结果
类型:string,值:100
类型:main.Integer,值:100
200

方法继承,组合(匿名字段是组合的特殊形式)

package main

import "fmt"

// 父类结构体 Car
type Car struct {
   
     
    weight int
    name string
}

// 父方法
func (c *Car) Run() {
   
     
    fmt.Println("Running")
}

// 子结构体 Bike
type Bike struct {
   
     
    // 组合(有名字)
    c Car
    wheel int
}

// 子结构体 Train
type Train struct {
   
     
    // 匿名
    Car
    wheel int
}

func main() {
   
     
    var bike Bike
    bike.c.weight = 100
    bike.c.name = "bike"
    bike.wheel = 2
    
    var train Train
    train.weight = 140000
    train.name = "train"
    train.wheel = 8
    
    fmt.Println(bike)
    // 方法继承
    bike.c.Run()
    fmt.Println(train)
    // 方法继承
    train.Run()
}

// 结果
{
   
     {
   
     100 bike} 2}
Running
{
   
     {
   
     140000 train} 8}
Running