GO学习笔记——结构体、方法、接口

GO中的结构体(struct)、方法(method)、接口(interface)与GO的面向对象特性紧密相关,而且三者之间关系层层递进,所以放在同一篇文章来介绍。

一、结构体(struct)

GO中的结构体形式上与C的结构体极为相似,但需要明白一点,GO中没有类的概念,支持面向对象编程是靠结构体来实现,所以GO中结构体与其他语言的类有着同等的地位。

1、声明

基本语法:

type 结构体名称 struct{

        field1 type

        field2 type

        ...

}

举个例子:

type Student struct{
    Name  string
    Age   int
    ID    string
}

2、实例创建

创建结构体变量有四种常用方法:

第一种(直接var):

var student1 Student

第二种(:= 和 {}):

student2 := Student{"Izelern", 21, "114514"}

第三种(new()和结构体指针):

var student3 *Student = new(Student)

(*student3).Name = "Izelern"
student3.Name = "Eliott"

(*student3).Age = 21
student3.Age = 18

//对于结构体指针,以上两种引用写法GO语法均允许

第四种(结构体指针与{}):

var student4 *Student = &Student{"Izelern", 21, "114514"}

var student5 *Student = &Student{}

(*student5).Name = "Izelern"
student5.Name = "Eliott"

(*student5).Age = 21
student5.Age = 18

注意:结构体指针访问字段的标准方法应该是(*结构体指针).字段名,但是GO中提供了简化的操作,也支持结构体指针.字段名, GO的编译器低层会将结构体指针.字段名转化为(*结构体指针).字段名

3、注意事项说明

1)结构体是值类型,也就是说在传参时如果直接使用,传进去的是参数是拷贝,内部修改无法改变外部值。

2)结构体的所有字段在内存中是连续的。

3)结构体和其他类型进行转换时,需要二者拥有相同字段,要求名字、个数、类型完全相同。

type A struct{
    Num int
}

type B struct{
    Num int
}

type C struct{
    Number int
}

type D struct{
    Num int
    Name string
}

type E struct{
    Num string
}

如上代码,A和B是可以进行强转的,即:

var a A
var b B

a = A(b)

但与C、D、E之间不能够进行强转。

4)对结构体进行type重定义后,Go会认为是新的数据类型,相互之间可以进行强转,原理见上一条。

type Person struct{
    Name string
}

type Per Person

func main(){
    var per1 Person
    var per2 Per
    per1 = Person(per2)
}

5)结构体的每个字段上可以加一个tag,该tag可以通过反射机制获取,常见的使用场景就是序列化和反序列化,后面反射的时候再细说。


二、方法(method)

Golang中,方法是作用于指定的数据类型上的,和相应数据类型绑定(不止可以绑定在结构体),包括自定义类型都可以有方法,int、string等基本数据类型也行。

1、声明

基本语法:
func (recevier type) methodName (参数列表) (返回值列表){

        方法体

        return 返回值

}

说明:

1)recevier type表示这个方法和type这个类型进行绑定,recevier为type类型的一个变量

2)除了与type类绑定外,方法的声明与函数别无二致

举个例子:

type Dog struct {
	Name   string
	Age    int
	Hungry bool
}

func (dog Dog) eat() {
	fmt.Println(dog.Name, "is eating")
}

func main() {
	dog := Dog{"Cuty", 8, true}
	dog.eat()
}

 运行上面代码得到输出为:

Cuty is eating

2、调用与传参

方法的调用需要靠绑定的type类实例来进行。
在调用过程中,参数传递是拷贝到方法内的,包括所绑定type类实例也是拷贝进去的,也就是说在方法内对传进的参数值进行修改,并不会改变外部实例的值,与函数是同理的。

 这里只讲解所绑定type类的实例如何进行传参的,看下面这个例子:

type Dog struct {
	Name   string
	Age    int
	Hungry bool
}

func (dog Dog) eat() {
	dog.Hungry = false
}

func (dog Dog) isHungry() bool {
	return dog.Hungry
}

func main() {
	dog := Dog{"Cuty", 8, true}
	dog.eat()

	if dog.isHungry() {
		fmt.Println(dog.Name, "is hungry")
	} else {
		fmt.Println(dog.Name, "is not hungry")
	}

}

运行后发现,代码的输出为:

说明方法eat()并未改变dog实例的Hungry值,理论上发生改变的是调用这个方法的实例的复制体,如果想要在方法内部改变传入参数的值,可以传入参数的指针,将上面代码中的eat()方法更改为:

func (dog *Dog) eat() {
	dog.Hungry = false
}

运行后发现,代码的输出变为:

说明对指针指向的实例进行了修改。

3、注意事项说明

对上文进行一个总结

1)结构体类型是值类型,在方法调用过程中,遵循的是值类型的传参机制,进行的是拷贝。如果希望在方法中修改结构体变量的值,则可以通过结构体指针进行,其余值类型同理,引用类型同指针使用原理相同。

2)方法的访问权限控制与函数一样,靠首字母大小写来区分。首字母小写,只能在当前包访问;首字母大写,可以在本包和其他包访问。

3)如果一个类型实现了String()方法,那么在fmt.Println这个类型实例时,会自动调用实现了的String()方法,见如下例子:

type Dog struct {
	Name   string
	Age    int
	Hungry bool
}

func (dog *Dog) String() string {
	str := fmt.Sprintf("Name=[%v]", dog.Name)
	return str
}

func main() {
	dog := Dog{"Cuty", 8, true}
	fmt.Println(&dog)
}

代码输出为:


三、接口

在GO中,多态主要是通过接口(interface)来体现的。

1、声明

基本语法:

type 接口名 interface{

        method1(参数列表) 返回值列表

        method2(参数列表) 返回值列表

}

实现接口方法:

func (T type) method1(参数列表) 返回值列表{

        //方法体

}

func (T type) method2(参数列表) 返回值列表{

        //方法体

}

接口里的方法都没有方法体,这些实现接口的结构体需要实现接口的全部方法。GO中接口不需要显示实现,只要含有接口类型的所有方法,那么这个变量就是实现了这个接口,所有没有implement关键字。另外,接口中不能有变量,这和Java中的接口是一样的。

举个例子:

type Dog struct {
	Name   string
	Age    int
	Hungry bool
}

type Cat struct {
	Name   string
	Age    int
	Hungry bool
}

type Alive interface {
	eat()
	drink()
}

func (d *Dog) eat() {
	d.Hungry = false
	fmt.Println("小狗吃骨头")
}

func (d Dog) drink() {
	fmt.Println("小狗喝水")
}

func (c Cat) eat() {
	c.Hungry = false
	fmt.Println("小猫吃鱼")
}

func (c Cat) drink() {
	fmt.Println("小猫喝牛奶")
}

func main() {
	var dog Dog
	var cat Cat
	var creature Alive = &dog
	creature.eat()
	dog.eat()
	creature = &cat
	creature.drink()
	cat.drink()
}

上述代码的输出为:

2、注意事项说明

1)接口本身不能创建实例,但可以指向一个实现了该接口的自定义类型变量(笔者认为这是多态的基础)

2)一个自定义类型将某个接口的全部方法实现,称该自定义类型实现了该接口

3)一个自定义接口只有实现了接口,才能将该自定义类型的实例赋值给接口类型

4)一个自定义类型可以实现多个接口

5)一个接口可以继承多个别的接口,但是一个自定义类型要实现该接口时,不仅要实现该接口的方法,还要实现所有它继承的接口

6)interface类型是引用类型,需要初始化,否则为nil

7)空接口interface{}没有任何方法,所有类型都实现了空接口

  • 29
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值