Go结构体&接口&反射(上)

简介: Go结构体&接口&反射(上)

Go结构体&接口&并发&反射


一、结构体struct


0、Type关键字

Golang中通过type关键词定义一个结构体,需要注意的是,数组和结构体都是值类型

Go语言中可以使用type关键字来定义自定义类型:

type myInt int//通过type关键字的定义,mylnt就是一种新的类型,它具有int的特性


类型别名规定:TypeAlias只是Type的别名,本质上TypeAlias与Type是同一个类型

type TypeAlias = Type


rune 和 byte 就是类型别名,他们的底层代码如下:

type byte = uint8
type rune = int32


1、struct定义及使用

Go通过结构体struct和interface实现oop(面向对象编程)

struct的成员(也叫属性或字段)可以是任何类型,如普通类型、复合类型、函数、 map、 interface、

struct等

使用type 和 struct关键字来定义结构体:

type struct_variable_type struct {
    member member_type
    member member_type
    .....
    member member_type
}
type Student struct {
    name string
    age int //小写私有成员(对外不可见)
    Class string //首字母大写则该成员为公有成员(对外可见)
}


声明与初始化:

var stu1 Student
stu1.name="zhangsan"
//...
var stu2 *Student= &Student{name:"zhangsan",age:10,Class:"1"} //返回指针,等同于new实例化操作
var stu3 = Student{name:"zhangsan",age:10,Class:"1"} 
var stu4 = new(Student)
//...


自定义‘构造函数’方法:

func Newstu(name1 string,age1 int,class1 string) *Student {
    return &Student{name:name1,age:age1,Class:class1}
}
func main() {
    stu1 := Newstu("zhangsan"",34,"math")
    fmt.Println(stu1.name)
}

注:go中的结构体内存布局和c结构体布局类似,每个成员的内存分布是连续的,结构体大小遵循对齐原则


2、struct tag

tag可以为结构体的成员添加说明或者标签便于使用,这些说明可以通过反射获取到

此外还可以方便进行json序列化操纵

type Student struct {
  ID     string `json:"id1"`
  Gender string `json:"gender"`
  Name   string `json:"name"`
  Sno    string `json:"son"`
}


获取tag信息:

var s1 = Student{}
  st := reflect.TypeOf(s1)
  field1 := st.Field(0)
  fmt.Println("key1:", field1.Tag.Get("json"))


json序列化操作:

// Json字符串转换成结构体
var str = `{"id1":"12","name":"李四","gender":"男","sno":"s001"}`
var s2 = Student{}
err := json.Unmarshal([]byte(str), &s2)
if err != nil {
    fmt.Printf("转换失败 \n")
} else {
    fmt.Printf("%#v \n", s2)
}

注:要实现结构体转换成字符串,必须保证结构体中的字段是公有的,也就是首字母必须是大写的,这样才能够实现结构体 到 Json字符串的转换。


3、struct匿名成员

结构体中,每个成员不一定都有名称,也允许字段没有名字,即匿名成员

匿名成员的一个重要作用,可以用来实现oop中的继承

同一种类型匿名成员只允许最多存在一个

当匿名成员是结构体时,且两个结构体中都存在相同字段时,优先选择最近的字段

type Person struct {
    Name string
    Age int
}
type Student struct {
    score string
    Age int
    Person // 匿名内嵌结构体
}
func main() {
    var stu = new(Student)
    stu.Age = 34 //优先选择Student中的Age
    fmt.Println(stu.Person.Age, stu.Age) // 0,34
}


4、struct继承

当结构体中的成员也是结构体时, 该结构体就继承了这个结构体,继承了其所有的方法与属性,当然有多个结构体成员也就是多继承

访问父结构中属性也使用“.”,但是当子结构体中存在和父结构中的字段相同时候,只能使用: "子结构体.父结构体.字段"访问父结构体中的属性,如上面示例的stu.Person.Age

继承结构体可以使用别名,访问的时候通过别名访问

type Person struct {
  Name string
  Age  int
}
type Teacher struct {
  Salary  int
  Classes string
}
type man struct {
  sex    string
  job    Teacher //别名,继承Teacher 这个时候就不是匿名了
  Person         //继承Person
}
func main() {
  var man1 = new(man)
  man1.sex = "man"
  man1.Age = 34
  man1.Name = "zhangsan"
  man1.job.Classes = "111"
  man1.job.Salary = 100000
  fmt.Println(man1, man1.job.Salary)
}


5、struct方法

Go方法是作用在接受者上的一个函数,接受者是某种类型的变量,因此方法是一种特殊类型的函数。

接收者可以是任何类型,不仅仅是结构体, Go中的基本类型(int, string, bool等)也是可以,或者说数组的别名类型,甚至可以是函数类型。但是, 接受者不能是一个接口类型,因为接口是一个抽象的定义,方法是一个具体实现。

一个类型加上它的方法等价于面向对象中的一个类。一个重要的区别是:在 Go 中,类型的代码和绑定在它上面的方法的代码可以不放置在一起,它们可以存在在不同的源文件, 唯一的要求是:它们必须是同一个包的。

类型 T(或 *T)上的所有方法的集合叫做类型 T(或 *T)的方法集。

因为方法是函数,所以同样的,不允许方法重载,即对于一个类型只能有一个给定名称的方法。但是如果基于接收者类型,是有重载的:具有同样名字的方法可以在 2 个或多个不同的接收者类型上存在,比如在同一个包里这么做是允许的

别名类型不能有它原始类型上已经定义过的方法(因为别名类型和原始类型底层是一样的)。

定义方法的格式

func(recv recevier_type(结构体))methodName(parameter_list)(return _value_list){}  


结构体的指针方法

接收者是指针时,方法可以改变接受者的值(或状态),函数也能做到

当接受者是一个值的时候,这个值是该类型实例的拷贝;如果想要方法改变接受者的数据,就在接受者的指针类型上定义该方法


二、接口interface


1、接口定义和使用

interface(接口)是golang最重要的特性之一, Interface类型可以定义一组方法,但是这些不需要实

现,并且interface不能包含任何变量

interface 是方法的集合,interface是一种类型,并且是指针类型,interface的 更重要的作用在于多态实现

接口定义:

type 接口名称 interface {
    method1 (参数列表) 返回值列表
    method2 (参数列表) 返回值列表
    ...
}

注:

  • 接口名:使用type将接口定义为自定义的类型名。Go语言的接口在命名时,一般会在单词后面添加er,如有写操作的接口叫Writer,有字符串功能的接口叫Stringer等,接口名最好突出该接口的类型含义。
  • 方法名:当方法名首字母是大写且这个接口类型名首字母也是大写时,这个方法可以被接口所在的包(package)之外的代码访问。
  • 参数列表、返回值列表:参数列表和返回值列表中的参数变量名是可以省略


接口的使用不仅仅针对结构体,自定义类型、变量等等都可以实现接口

要实现一个接口/使用接口对象, 接口接收的对象类型就必须实现该接口里面的所有方法

//定义接口
type Skills interface {
    Running()
    Getname() string
}
type Student struct {
    Name string
    Age int
}
// 实现接口
func (p Student) Getname() string { //实现Getname方法
    fmt.Println(p.Name)
    return p.Name
}
func (p Student) Running() { // 实现 Running方法
    fmt.Printf("%s running", p.Name)
}
// 使用接口
func main() {
    var skill Skills
    var stu1 Student
    stu1.Name = "zhangsan"
    stu1.Age = 34
    skill = stu1
    skill.Running() //调用接口
}


2、空接口

如果一个接口没有任何方法,我们称为空接口,由于空接口没有方法, 任意结构体都隐式地实现了空接口

// 空接口表示没有任何约束,任意的类型都可以实现空接口
type EmptyA interface {
}
func main() {
  var a EmptyA
  var str = "你好golang"
  // 让字符串实现A接口
  a = str
  fmt.Println(a)
}


golang中空接口也可以直接当做类型来使用,可以表示任意类型。相当于Java中的Object类型:

var a interface{}
a = 20
a = "hello"
a = true


空接口可以作为函数的参数,使用空接口可以接收任意类型的函数参数:

// 空接口作为函数参数
func show(a interface{}) {
    fmt.println(a)
}


接口的值(简称接口值)是由一个具体类型和具体类型的值两部分组成的。这两部分分别称为接口的动态类型和动态值。

如果我们想要判断空接口中值的类型,那么这个时候就可以使用类型断言,其语法格式:

x.(T)//X:表示类型为interface{}的变量  T:表示断言x可能是的类型


// 类型断言
var a interface{}
a = "132"
value, isString := a.(string)
if isString {
    fmt.Println("是String类型, 值为:", value)
} else {
    fmt.Println("断言失败")
}

使用switch语句来实现类型判断

func Print2(x interface{})  {
  switch x.(type) {
  case int:
    fmt.Println("int类型")
  case string:
    fmt.Println("string类型")
  case bool:
    fmt.Println("bool类型")
  default:
    fmt.Println("其它类型")
  }
}


注:类型.(type) 只能结合switch语句使用

空接口如果值类型为切片,无法直接通过索引获取数组中的内容,只能使用类型断言:

// 这个时候我们就可以使用类型断言了
hobbyValue,ok := userInfo["hobby"].([]string)
if ok {
    fmt.Println(hobbyValue[0])
}


3、接口多态及嵌套

go语言中interface是实现多态的一种形式,所谓多态, 就是一种事物的多种形态

同一个interface,不同的类型实现,都可以通过接口进行统一调用,也就是多态

func main() {
  var skill Skills
  var stu1 Student
  var t1 Teacher
  t1.Name = "zhangsan"
  stu1.Name = "lisi"
  stu1.Age = 22
  skill = stu1
  skill.Running()
  skill = t1
  t1.Running()
}


go语言中的接口可以嵌套,可以理解为继承,子接口拥有父接口的所有方法

如果使用该子接口,必须将父接口和子接口的所有方法都实现

接口的定义也支持组合嵌套(多嵌套)

type Skills interface {
    Running()
    Getname() string
}
type Test interface {
    sleeping()
    Skills //继承Skills
}


相关文章
|
18天前
|
Go 开发工具
百炼-千问模型通过openai接口构建assistant 等 go语言
由于阿里百炼平台通义千问大模型没有完善的go语言兼容openapi示例,并且官方答复assistant是不兼容openapi sdk的。 实际使用中发现是能够支持的,所以自己写了一个demo test示例,给大家做一个参考。
|
18天前
|
程序员 Go
go语言中结构体(Struct)
go语言中结构体(Struct)
93 71
|
1月前
|
存储 Rust Go
Go nil 空结构体 空接口有什么区别?
本文介绍了Go语言中的`nil`、空结构体和空接口的区别。`nil`是预定义的零值变量,适用于指针、管道等类型;空结构体大小为0,多个空结构体实例指向同一地址;空接口由`_type`和`data`字段组成,仅当两者均为`nil`时,空接口才为`nil`。
Go nil 空结构体 空接口有什么区别?
|
1月前
|
编译器 Go
探索 Go 语言中的内存对齐:为什么结构体大小会有所不同?
在 Go 语言中,内存对齐是优化内存访问速度的重要概念。通过调整数据在内存中的位置,编译器确保不同类型的数据能够高效访问。本文通过示例代码展示了两个结构体 `A` 和 `B`,尽管字段相同但排列不同,导致内存占用分别为 40 字节和 48 字节。通过分析内存布局,解释了内存对齐的原因,并提供了优化结构体字段顺序的方法,以减少内存填充,提高性能。
41 3
|
3月前
|
Go
Go to Learn Go之反射
Go to Learn Go之反射
45 8
|
3月前
|
存储 Go
Go to Learn Go之接口
Go to Learn Go之接口
34 7
|
3月前
|
Go
Go to Learn Go之结构体
Go to Learn Go之结构体
40 5
|
3月前
|
存储 Shell Go
Go语言结构体和元组全面解析
Go语言结构体和元组全面解析
|
3月前
|
存储 Go
Go: struct 结构体类型和指针【学习笔记记录】
本文是Go语言中struct结构体类型和指针的学习笔记,包括结构体的定义、成员访问、使用匿名字段,以及指针变量的声明使用、指针数组定义使用和函数传参修改值的方法。
|
4月前
|
存储 设计模式 安全
空结构体:Go 语言中的轻量级占位符
【8月更文挑战第31天】
50 0