go语言只支持封装, 不支持继承和多态. Java是典型的支持面向对象的语言之一, 他本身支持面向对象的三大特征, 封装继承和多态; 到了Python中, Python显式的支持封装和继承, Python多态的特性大家争论不休, 虽然Python没有显式的多态语法, 但是由于Python这种动态语言的设计, 他本身就是多态的; 到了Go这里, 却只支持封装了, 与其说Go语言是一种面向对象的语言, 倒不如说他是一种面向接口的语言
go语言没有class, 只有struct, Go语言中也没有构造函数的说法
定义结构体
在结构体中, 可以为不同的key定义不同的数据类型, 结构体是由一系列具有相同类型或不同类型的数据构成的数据集合
结构体更类似于在传统的分层开发领域中的module层(实体层), 一般情况下, 实体层中的每个成员属性都对应了数据库中数据表的一个字段, 实力层存在的意义在于以对象的形式进行传值. 拿三层开发来说, 一般分为表示层/接口层, 业务逻辑层, 数据访问层. 每层与每层之间的数据流转正是通过引入的实体层, 实例化实体层的对象来实现的. 结构体之于Go, 类似于实体层中的类之于Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package main
import "fmt"
type employee struct { id int name string age int department string city string
}
func main() { e := employee{id:123, name:"larry", age:28, department:"paas", city:"TangShan"} ee := employee{id:234, name:"sam", age:45, department:"daas", city:"BeiJing"} fmt.Println(e) fmt.Println(ee) }
|
执行结果:
1 2
| {123 larry 28 paas TangShan} {234 sam 45 daas BeiJing}
|
访问结构体成员
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| package main
import "fmt"
type employee struct { id int name string age int department string city string
}
func main() { var e1 employee var e2 employee
e1.id = 123 e1.name = "larry" e1.age = 28 e1.department = "paas" e1.city = "TangShan"
e2.id = 234 e2.name = "sam" e2.age = 45 e2.department = "daas" e2.city = "BeiJing"
fmt.Println(e1.name, e1.age) fmt.Println(e2.name, e2.age) }
|
执行结果:
结构体作为函数参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package main
import "fmt"
type employee struct { id int name string age int department string city string
}
func printEmpInfo(emp employee) { fmt.Println(emp.name, emp.city) }
func main() { e := employee{id:123, name:"larry", age:28, department:"paas", city:"TangShan"} ee := employee{id:234, name:"sam", age:45, department:"daas", city:"BeiJing"}
printEmpInfo(e) printEmpInfo(ee) }
|
结构体用作函数参数的时候, 最像三层开发中的实体层用法
结构体指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| package main
import "fmt"
type employee struct { id int name string age int department string city string
}
func main() { e := employee{id:123, name:"larry", age:28, department:"paas", city:"TangShan"}
var e1 *employee
e1 = &e
fmt.Println(e1.age, e.age)
e1.age = 29 fmt.Println(e1.age, e.age) }
|
执行结果:
方法
在Python中, 方法和函数的区别可以简单理解为在类中的函数就叫方法. 在Go语言中, 什么是方法呢, 可以简单认为, 为结构体定义的函数就叫方法
与其他语言不同的是, 为结构体定义的方法, 从形式上看不是写在结构体里面的, 而是写在结构体外面的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| package main
import "fmt"
type employee struct { id int name string age int department string city string
}
func (emp employee) printEmpInfo(arg string) { fmt.Printf("我是形式参数 %s\n", arg) fmt.Println(emp.name) fmt.Println(emp.age) }
func main() { e := employee{id:123, name:"larry", age:28, department:"paas", city:"TangShan"} e.printEmpInfo("PolarSnow") }
|
执行结果:
1 2 3
| 我是形式参数 PolarSnow larry 28
|
上面的demo中, e.printEmpInfo("PolarSnow")
e对象调用了printEmpInfo方法, 并显式传递了PolarSnow参数, 其实在背后, 在存在一个隐式传参, 就是e把自己传递给了方法定义中的(emp employee)
, 类似于Java中的this, 和Python中的self
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package main
import "fmt"
type employee struct { id int name string age int department string city string
}
func printEmpInfo(emp employee, arg string) { fmt.Printf("我是形式参数 %s\n", arg) fmt.Println(emp.name) fmt.Println(emp.age) }
func main() { e := employee{id:123, name:"larry", age:28, department:"paas", city:"TangShan"} printEmpInfo(e, "PolarSnow") }
|
这么写效果是一样的, 只是调用的时候是直接调用函数, 把结构体对象当做形式参数传递, 而不是调用结构体自己的方法, 将自己隐式传递到方法中
结构体方法中隐式传参的问题
注意: 默认情况下, Go语言所有参数都是传值, func (emp employee) printEmpInfo(arg string)
也不例外, 同样是值传递
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package main
import "fmt"
type employee struct { id int name string age int department string city string
}
func (emp employee) setEmpAge(age int) { emp.age = age fmt.Println("我是结构体方法里的值", emp.age) }
func main() { e := employee{id:123, name:"larry", age:28, department:"paas", city:"TangShan"} e.setEmpAge(29) fmt.Println("我是结构体方法外面的值", e.age) }
|
执行结果:
1 2
| 我是结构体方法里的值 29 我是结构体方法外面的值 28
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| package main
import "fmt"
type employee struct { id int name string age int department string city string
}
func (emp *employee) setEmpAge(age int) { emp.age = age fmt.Println("我是结构体方法里的值", emp.age) }
func main() { e := employee{id:123, name:"larry", age:28, department:"paas", city:"TangShan"} e.setEmpAge(29) fmt.Println("我是结构体方法外面的值", e.age) }
|
执行结果:
1 2
| 我是结构体方法里的值 29 我是结构体方法外面的值 29
|
注意: 只有指针才可以改变结构体中的内容
nil指针也是可以调用方法的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package main
import "fmt"
type employee struct { id int name string age int department string city string
}
func (emp *employee) setEmpAge(age int) { if emp == nil { fmt.Println("employee 对象为 nil 指针") return } }
func main() { var e *employee e.setEmpAge(29) }
|
执行结果:
值接收者 vs 指针接收者
- 要改变内容必须使用指针接收者
- 结构过大也需要考虑使用指针接受者
- 一致性: 如果有指针接收者, 最好都是指针接收者