本篇内容:匿名属性、方法、接口
久违的面向对象编程
面向对象编程
面向对象编程离不开继承、封装、多态
go中没有这些概念,它通过其他的方式实现这些特性
继承:匿名属性
封装:方法
多态:接口
匿名属性(匿名字段、匿名组合)
匿名字段可以是自定义结构体类型、内置类型、指针类型
// 匿名属性/字段
package main
import "fmt"
// 自定义结构体User
type User struct {
id int // 结构体成员变量,不需要通过var声明,普通变量需要
no int
username string
password string
}
// Worker
type Worker struct {
User // 匿名字段,只有结构体类型,没有变量名称,表示Worker继承了User
int // 年龄,非结构体匿名字段
no int
dept string
}
func main() {
// 初始化方式和普通的结构体类型初始化相似
var worker Worker = Worker{User: User{id: 1, no: 10001, username: "yw"},
int: 32, no: 30001, dept: "市场部"} // int: 32,非结构体匿名字段-年龄赋值
fmt.Println("worker =", worker) // worker = {{1 10001 yw } 32 30001 市场部}
// 访问成员变量
username := worker.username
dept := worker.dept
fmt.Printf("username = %v, dept = %v\n", username, dept) // username = yw, dept = 市场部
// 重名字段
workerNo := worker.no
userNo := worker.User.no // 匿名变量通过User访问,相当于访问父类的属性
fmt.Printf("workerNo = %v, userNo = %v\n", workerNo, userNo) // workerNo = 30001, userNo = 10001
}
方法
方法比函数在func后面多了一个括号(),声明接收类型,
表示该函数绑定于当前类型,也可以是自定义类型别名,
带有接受者的函数叫做方法
函数更面向过程
方法更面向对象
我们还是来看代码吧
package main
import "fmt"
// 自定义结构体User
type User struct {
id int // 结构体成员变量,不需要通过var声明,普通变量需要
no int
username string
password string
}
// 给User类型添加一个eat方法
func (obj User) eat(food string) { // 这里(obj User)接收的是调用该方法的对象
fmt.Println(obj.username, "吃", food) // obj是调用者本身
}
func (obj User) changeByValue() {
obj.username = "hh"
}
func (obj *User) changeByPointer() { // 接受类型可以是显式的指针类型
(*obj).username = "hh"
// 或
// obj.username = "hh" // 因为调用转化的存在:(指针←→变量)
}
type userPointer *User
// func (up userPointer) doSth() {} // 接受类型不可以是隐式的指针类型
func main() {
var user User = User{id: 1, no: 10001, username: "yw"}
user.eat("西瓜") // yw eat 西瓜
user.changeByValue()
fmt.Println(user) // {1 10001 yw }
userP := &user
userP.changeByPointer()
fmt.Println(user) // {1 10001 hh }
}
可以存在接受者类型不一样的重名方法,我叫go的方法重载
方法集
类型的方法集:是指可以被该类型的值调用的所有方法的集合
即所有接受者类型为当前类型的方法的集合
继承
匿名字段继承结构体类型时,也继承了父结构体类型的方法集
// 方法继承
package main
import "fmt"
// 自定义结构体User
type User struct {
id int // 结构体成员变量,不需要通过var声明,普通变量需要
no int
username string
password string
}
// Worker
type Worker struct {
User // 匿名字段,只有结构体类型,没有变量名称,表示Worker继承了User
int // 年龄,非结构体匿名字段
no int
dept string
}
func (obj User) eat(food string) { // 这里(obj User)接收的是调用该方法的对象
fmt.Println("用户", obj.username, "吃", food) // obj是调用者本身
}
// 重名方法
func (obj Worker) eat(food string) { // 这里(obj User)接收的是调用该方法的对象
fmt.Println("工人", obj.username, "吃", food) // obj是调用者本身
}
func main() {
// 匿名字段继承结构体类型时,也继承了父结构体类型的方法集
var worker Worker = Worker{User: User{id: 1, no: 10001, username: "yw"}, int: 32, no: 30001, dept: "市场部"}
worker.eat("西瓜") // 重写后的方法 // 就近调用Worker接受者类型方法:工人 yw 吃 西瓜
worker.User.eat("西瓜") // 调用父结构体eat方法:用户 yw 吃 西瓜
}
方法值和方法表达式
package main
import "fmt"
// 自定义结构体User
type User struct {
id int // 结构体成员变量,不需要通过var声明,普通变量需要
no int
username string
password string
}
// Worker
type Worker struct {
User // 匿名字段,只有结构体类型,没有变量名称,表示Worker继承了User
int // 年龄,非结构体匿名字段
no int
dept string
}
func (obj Worker) doSth() {
fmt.Println(obj)
}
func main() {
var worker Worker = Worker{User: User{id: 1, no: 10001, username: "yw"},
int: 32, no: 30001, dept: "市场部"}
// 方法值
doSth1 := worker.doSth // 方法值声明时就传入了调用对象
doSth1() // {{1 10001 yw } 32 30001 市场部}
// 方法表达式
doSth2 := Worker.doSth // 方法表达式调用时才传入了调用对象
doSth2(worker) // {{1 10001 yw } 32 30001 市场部}
}
接口
type 接口名称 interface {}
结构体绑定有接口的实现方法,接口就可以实例化为该结构体,并调用方法
接口可以继承
package main
import "fmt"
// 自定义接口Animal
type Animal interface {
doSth()
}
// 自定义接口Animal
type Bird interface {
Animal // 通过匿名字段,继承接口Animal
}
// 自定义结构体Parrot鹦鹉
type Parrot struct {
id int
name string
language string
}
// 自定义结构体User
type User struct {
id int
no int
username string
password string
}
func (obj User) doSth() {
fmt.Println("用户信息:", obj)
}
func (obj Parrot) doSth() {
fmt.Println("鹦鹉信息:", obj)
}
func main() {
var animal Animal
var user User = User{id: 1, no: 10001, username: "yw"}
// 结构体绑定有接口的实现方法,接口就可以实例化为该结构体,并调用方法
animal = user
animal.doSth() // 用户信息: {1 10001 yw }
// 多态
var parrot Parrot = Parrot{id: 3, name: "Polly", language: "英语"}
animal = parrot
animal.doSth() // 鹦鹉信息: {3 Polly 英语}
}
空接口
interface{}
空接口可以接受任何变量,所有类型都继承空接口
类似Java中的Object类型
类型断言
data, ok := obj.(type)
判断obj是否属于type类型,返回数据和true或false
// 空接口
var obj interface{} = "abc"
// 接口断言:data, ok := obj.(type),判断obj是否属于type类型,返回数据和true或false
intData, intOk := obj.(int)
fmt.Println(intData, intOk) // 0 false
stringData, stringOk := obj.(string)
fmt.Println(stringData, stringOk) // abc true
不当之处,请予指正。