当前位置: 首页 > news >正文

【Go基础】结构体

1. 结构体引入

Golang也支持面向对象编程,但是和传统的面向对象有区别,并不是像Java、C++那样纯粹的面向对象语言,而是通过特别的手段实现面向对象特点。

Golang没有类(Class)的概念,但是提供了结构体(struct),和其他编程语言中的类(class)有同等地位,可以理解为 :Go基于struct"拐弯抹角"的实现了面向对象的特点。

Golang面向对象编程非常简洁,去掉了传统OOP语言的 方法重载、构造函数、和析构函数、this、super一系列东西,但是保留了封装、继承、多态的特性,只是和其他语言的实现方式不同。

2. 结构体的定义

使用 type​ 和 struct​定义一个结构体

type People struct {
}

People就成了一个结构体,当然可以为People添加一些字段

type People struct {
	name string
	age string
	addr string
}

定义之后如何声明呢 ?

有以下几种方式 :

  1. 使用 var 变量名 结构体类型

    var people People
    people.name = "小明"
    people.age = 18
    fmt.Println(people) 
    // {小明 18}
    
  2. 变量名 := new(结构体类型)

    new返回的指向特定类型的指针,所以这种方式返回的是 指向一个People结构体 的指针

    people := new(People)
    people.name = "小明"
    people.age = 18
    fmt.Println(people) 
    // &{小明 18}
    

    为什么people是指针,还能使用 .​ 来访问结构体中的变量呢?使用 (*people).name 、(*people).age​当然可以访问,只是这种不好写,Go为我们进行了优化。

    如果第一种方式和这种方式让你选一种,你用哪一个呢?当一个结构体中的字段特别多时,直接创建该结构体会占用大量空间,但是指针不一样,指针固定大小。所以以上两种第二种常用。不过还有其他方式。

  3. 变量名 := 结构体类型{字段1:值, 字段1:值,}

    使用这种方式跟第一种差不多,都是直接开辟空间,不过这种方式可以直接对属性赋值

    (左边用var​还是:=​都一样)

    people := People{
    	name: "小明",
    	age: 18,
    }
    fmt.Printf("people的类型为: %T\n", people)
    // main.People
    
    // 这样也一样
    var people1 People = People{
    	name: "小明",
    	age: 18,
    }
    

    这可不是json,字段名不加双引号,最后一个字段后要要加逗号。

  4. 变量名 := &结构体类型{字段1:值, 字段2:值,}

    这种方式和第二种像,返回的是一个指向结构体的指针。

    people := &People {
    	name: "小明",
    	age: 18,
    }
    fmt.Printf("people的类型为: %T\n", people)
    //  *main.People
    

这四种方式选择哪一种,这要看习惯与需求,熟练用其中一种,可以看懂其他的方式就行。

初始化时可以直接列出全部的字段值,这种方式的缺点是 :无法指定字段,且一次性需要给全部字段赋值,如果后续由增加或删除,会出问题。

type People struct {
	name string
	age int
}
func main() {
	people := &People {
		"小明",
		18,
	}
}

匿名字段 :没有名字的字段。

type People struct {
	string
	int
}
func main() {
	people := &People {
		"小明",
		18
	}

}

赋值时需要严格按照字段类型与顺序,一般不用匿名字段,因为可读性太差,且如果出现多个string int类型,可读性更差

3. 结构体之间的转换

当两个不同类型的结构体中,字段数量字段名字段顺序字段类型全部相同时,这两个字段可以相互转换。

type People struct {
	name   string
	age    int
	height float32
}

type Monkey struct {
	name   string
	age    int
	height float32
}

func main() {
	// 声明一个People类型的结构体
	people := &People{
		name:   "小明",
		age:    18,
		height: 180.4,
	}
	// 将上述结构体变量转换为 *Monkey 类型
	monkey := (*Monkey)(people)
	fmt.Println(monkey)
	// 打印结果:  &{小明 18 180.4}

}

两个结构体是否能相互转换 :

字段数、字段名、字段顺序、字段类型​ 全部相同,缺一个就无法编译。

4. 结构体嵌套

说人话,结构体里还有结构体。

type Address struct {
	province string
	city     string
}
type People struct {
	id      int
	name    string
	age     int
	address Address
}
func main() {
	people := &People{
		id:   1,
		name: "小明",
		age:  18,
		address: Address{
			province: "河北",
			city:     "衡水",
		},
	}
	fmt.Println(people)
}

需要注意的是,People中的address字段如果是指针,在赋初值时就申请指针,是结构体就直接赋值结构体。

可以直接people.address.city访问嵌套结构体中的值.

fmt.Println(people.address.city)

这个经常使用,因为仅仅使用一个结构体无法描述出一个复杂的类型,就可以分为多个字段。

结构体中也可以有结构体数组,不过很少用这种方式一个一个new,都是从前端传来的json直接转换。

package main

import (
	"fmt"
	"strconv"
)

type Phone struct {
	name  string
	price int
}

func NewPhone(name string, price int) Phone {
	return Phone{
		name:  name,
		price: price,
	}
}

type People struct {
	name   string
	phones []Phone
}

func NewPeople(name string, phones []Phone) People {
	return People{
		name:   name,
		phones: phones,
	}
}

func main() {
	// 创建一个People数组
	people := NewPeople("小明", make([]Phone, 5, 200))
	// 循环给people数组中的值赋值
	for i := 0; i < 5; i++ {
		phoneName := "小米" + strconv.Itoa(i)
		phone := NewPhone(phoneName, 1000*(i+1))
		//
		people.phones[i] = phone
		// 如果在初始化切片时指定长度为0或者小于5,使用append()扩容
		// people.phones = append(people.phones, phone)
	}
	fmt.Println(people)

}

5. 方法的引入

方法是作用在指定的数据类型上、和指定的数据类型绑定,因此自定义类型都可以有方法,通常给结构体定义一些方法,以便外部调用,就像Java中的setter/getter方法、C++中的构造函数、析构函数一样。当然,Go语言中的方法并没有放在结构体中,而是和结构体分离。

形式:

func (变量名 : 自定义类型) 方法名(参数列表) (返回值列表) {

// 方法体

}

第一个括号中的变量不需要传入,而是==“谁调用,这个变量就是谁”==

当然可以不使用第一个变量。

给People定义一个初始化方法:

type People struct {
	name string
	age int
}
func newPeople(name string, age int) (*People) {
	return &People {
		people.name = name
		people.age = age
	}
}
func main() {
	people := newPeople("小明", 18)
}

这个newPeople方法接收两个变量,name和age,通过这两个变量创造一个Person对象并返回,以后再也不用频繁的写初始化方法了。

以下是使用第一个变量的例子 :

func (people *People) SetAge(age int) {
	people.age = age
}

这个方法的调用者必须是People类型的指针,否则会报错。

people := newPeople("小明", 18)
fmt.Println(people) // &{小明 18}
people.SetAge(10)
fmt.Println(people.age) // 10

就是方法的特殊性,让我们可以对结构体进行各种操作。你可以单独定义一个文件,在这个文件中完成对结构体的封装。

image

方法和函数的区别 :

  • 方法被调用者影响。
  • 函数与没有具体的调用者。

相关文章:

  • 重庆最便宜的网站建设/怎样做seo搜索引擎优化
  • dedecms做的网站如何上线/常用的seo工具的是有哪些
  • wordpress无法访问/如何做好推广工作
  • 妈妈网站源码/短视频营销策划方案
  • 大气企业网站欣赏/网络运营是什么专业
  • 专门做礼物的网站/自贡网站seo
  • 2022年个人年终总结(一)
  • 如何使用OpenDRIVE
  • 邂逅Vue.js开发
  • linux基本功系列之pwd命令实战
  • 注意力机制
  • aardio - 升级bindConfig函数,支持多属性和多子组件
  • 《Unity Shader 入门精要》第2章 渲染流水线
  • 【ROS】dynamic_reconfigure配置详细说明
  • 【JavaSE】Java反射机制详解
  • Unity 之 Addressable可寻址系统 -- 资源加载和释放
  • 静态链接过程分析
  • Java对象引用级别