这是我参与「第三届青训营 -后端场」笔记创作活动的第7篇笔记。
一、封装
1.1、定义对象以及创建对象
package main
import "fmt"
//定义类型
type T struct {
name string
sex int
}
func main() {
//定义对象
//创建对象方式一:键值对形式
t1 := T{name: "changlu",sex: 123}
//创建对象方式二:直接顺序传入
t2 := T{"changlu",123}
fmt.Println(t1, t2)
//创建对象方式三:不带有属性值
s := new(Student)
}
类方法调用(T与*T区别)
提问:对象的方法一个采用T,一个采用*T,有什么区别?
结论:前者是值传递,后者是引用传递,调用后者*T才会对调用对象的属性进行改变!
package main
import "fmt"
//定义对象
type T struct {
name string
}
//这里是值传递,不会对实际的对象修改值
func (t T) method1() {
t.name = "liner"
}
//这是引用传递,会对原始对象进行修改
func (t *T) method2() {
t.name = "liner liner"
}
func main() {
t := T{"changlu"}
//测试方法一
fmt.Println("method1前:", t.name)
t.method1()
fmt.Println("method1后:", t.name)
//测试方法二
fmt.Println("method1前:", t.name)
t.method2()
fmt.Println("method2后:", t.name)
}
1.2、方法值:绑定了对象值的方法
示例
package main
import "fmt"
//定义对象
type Student struct {
age int
}
//这里是值传递,不会对实际的对象修改值
func (s Student) ageDifference(student Student) int {
return s.age - student.age
}
func main() {
student1 := Student{18}
student2 := Student{19}
//方式一:对象.方法(其他对象)
fmt.Println(student1.ageDifference(student2))
//方式二:拿到带有对象的方法
stu2Diff := student2.ageDifference
fmt.Println(stu2Diff(student1)) //这里实际上就相当于 student2.ageDifference(student1)
}
应用场景
在一个包的API需要一个函数值、且调用方希望操作的是某一个绑定了对象的方法的话,方法"值"会非常实用.
举例来说,下面例子中的time.AfterFunc这个函数的功能是在指定的延迟时间之后来执行一个(译注:另外的)函数。且这个函数操作的是一个Rocket对象r:
type Rocket struct { /* ... */ }
func (r *Rocket) Launch() { /* ... */ }
r := new(Rocket)
比较麻烦写法:
time.AfterFunc(10 * time.Second, func() { r.Launch() })
简洁写法:直接传入函数(推荐)
time.AfterFunc(10 * time.Second, r.Launch)
1.3、方法表达式
好处:灵活来指定调用者与目标调用者。
测试
定义方法表达式与使用:
package main
import "fmt"
//定义对象
type Student struct {
age int
}
//这里是值传递,不会对实际的对象修改值
func (s Student) ageDifference(student Student) int {
return s.age - student.age
}
func main() {
//获取方式:通过struct名拿到的,所以其为:方法表达式
ageDiff := Student.ageDifference
//使用方法表达式来进行前后对象的调用
s1 := Student{18}
s2 := Student{19}
fmt.Println(ageDiff(s1, s2)) //等价于:s1.ageDifference(s2)。妙!
}
使用场景
需求:使用一个通用的方法来通过参数传递的形式进行年龄加或者减。
- 使用一个通用方法来根据传入的参数true或false来接收指定的方法表达式并进行执行。
package main
import "fmt"
//定义对象
type Student struct {
age int
}
//年龄加法
func (s Student)add(student Student) int {
return s.age + student.age
}
//年龄减法
func (s Student)minus(student Student) int {
return s.age - student.age
}
//计算(使用方法表达式来进行接收)
func calculate(s1 Student, s2 Student, oper bool) int {
//定义一个方法表达式
var op func(s1,s2 Student) int //s1表示所属对象;s2表示方法参数;int表示返回值
//若是true则使用add,若是false则使用minus
if oper {
op = Student.add
}else {
op = Student.minus
}
return op(s1, s2)
}
func main() {
//使用方法表达式来进行前后对象的调用
s1 := Student{18}
s2 := Student{19}
fmt.Println(calculate(s1,s2, true)) //进行年龄相加
fmt.Println(calculate(s1,s2, false)) //进行年龄相减
}
二、继承
子类继承父类,并重写方法:
package main
import "fmt"
//定义一个父类
type Human struct {
name string
sex string
}
//父类定义方法
func (this *Human) Eat() {
fmt.Println("Human eat ...")
}
func (this *Human) walk() {
fmt.Println("Human walk ...")
}
//定义子类
type Man struct {
Human
age int
}
//重写父类方法(实现多态)
func (this *Man) Eat() {
fmt.Println("Man eat ...")
}
func (this *Man) walk() {
fmt.Println("Man walk ...")
}
func (this *Man) getAge() {
fmt.Println("Man age is ", this.age)
}
func main() {
//父类
var human Human
human = Human{"人类", "男"}
human.Eat()
human.walk()
//子类
var man Man
man.age = 18
//使用父类的方法
man.Eat()
man.walk()
man.getAge()
}
三、多态
3.1、多态案例
案例:定义一个接口,接着两个类去实现接口方法,通过变量来修改引用指向,实现多态。
- 规范1:一旦实现某个接口中的方法,那么其他接口方法都要实现
- 规范2:给变量传递一定要传递指针,如:animal = &Dog{"red"}
package main
import "fmt"
//定义一个接口(其中定义方法)
type AnimalIF interface {
Sleep()
getColor()
GetType()
}
//狗实现接口:func (this *Dog) Sleep(){}
type Dog struct {
color string
}
//规范1:一旦实现某个接口中的方法,那么其他接口方法都要实现
func (this *Dog) getColor() {
fmt.Println("Dog color:", this.color)
}
func (this *Dog) GetType() {
fmt.Println("Dog")
}
func (this *Dog) Sleep() {
fmt.Println("Dog 睡觉")
}
//猫也定义一套实现
type Cat struct {
color string
}
func (this *Cat) getColor() {
fmt.Println("Cat color:", this.color)
}
func (this *Cat) GetType() {
fmt.Println("Cat")
}
func (this *Cat) Sleep() {
fmt.Println("Cat 睡觉")
}
func main() {
var animal AnimalIF
//规范2:给变量传递一定要传递指针
animal = &Dog{"red"}
animal.Sleep()
animal.getColor()
animal.GetType()
//将接口引用指向猫
animal = &Cat{"yellor"}
animal.Sleep()
animal.getColor()
animal.GetType()
}
3.2、多态在方法中使用
案例:我们可以在方法中使用多态来传递对应的实现引用。
//多态指针传入
func testAnimalMethods(animal AnimalIF) {
animal.Sleep()
animal.getColor()
animal.GetType()
}
func main() {
var animal AnimalIF
animal = &Dog{"red"}
testAnimalMethods(animal)
animal = &Cat{"yellor"}
testAnimalMethods(animal)
}
参考
[1] 7、面向对象特征