这是我参与「第三届青训营 -后端场」笔记创作活动的的第4篇笔记;主要学习了Go语言如何用面向对象的方式编程,还学习了Go如何做包管理.
struct 基本定义与使用
struct是go语言为我们提供的可以自定义的一种类型,该类型可以封装多个基本数据类型,可以用来存放一个事物的不同属性,声明和使用方式几乎和C语言一致,不愧是号称现代C语言。。。
tips: struct 传一个结构体参数是值传递,不像slice、map一样,变量名本身是一个指针,常用的办法是传一个指向该struct的指针。
package main
import (
"fmt"
)
//type 关键字, 声明一中新的数据类型
type myint int
type Book struct {
title string
auth string
}
func set_book(book *Book) {
// 居然不是->
book.auth = "me"
}
func main() {
var a myint = 10;
fmt.Printf("a types = %T\n",a)
var book1 Book
book1.title = "Golang"
book1.auth = "zhangsan"
fmt.Println(book1)
set_book(&book1)
fmt.Println(book1)
}
类的表示与封装
Go在面向对象这块做了很多减法,没有public等权限,根据属性名的首字母大小来判断是否对外开放,没有构造函数、析构函数,所有事情都交给GC去处理;没有复杂的继承、多态等语法支持,利用组合的方式实现继承,利用接口的方式实现多态。
基本方法: 通过在func后加一个()来绑定该函数的所属对象(...好奇怪的语法)
package main
import (
"fmt"
)
type Hero struct {
Name string
Ad int
Level int
}
// this 是形参名
// GetName 方法不需要修改对象,所以单纯值拷贝就够了
func (this Hero) GetName() string {
return this.Name
}
// set需要更改对象的属性,所以传入的是指针
func (this *Hero) SetName(new_name string) {
this.Name = new_name
}
func (this Hero) Show() {
fmt.Println(this)
}
func main() {
// 创建一个对象
myHero := Hero{Name:"zhangsan", Ad:1, Level:3 }
myHero.Show();
fmt.Println(myHero.GetName())
myHero.SetName("lisi")
myHero.Show()
}
// 好像是说不建议用this作为参数名,虽然不是关键字,但是产生误导??!!
tips: 和函数的命名方式同理,如果是外部调用的类名或者类中的属性名,需要大写,对外隐藏细节的话就要小写
继承
方式很简洁,这就是所谓的大道至简吗?
无需特定指名,只要实现了接口的所有函数就默认实现了该接口。。。好奇怪的机制
package main
import (
"fmt"
)
type Human struct {
Name string
Sex string
}
func (this Human) Eat() {
fmt.Println("Human is eating")
}
func (this Human) Walk() {
fmt.Println("Human is walking")
}
// 父类继承子类
type Superman struct {
Human // 只写一个类型,代表当前类继承了Human类
level int
}
// 此时可以重载父类的方法
func (this Superman) Eat() {
fmt.Println("Superman doesn't need to eat")
}
func main() {
// 创建一个对象, Name: Sex: 这些可以省略
h := Human{Name:"zhangsan", Sex:"男" }
h.Eat();
h.Walk()
// 定义一个子类对象
s := Superman{ Human{"super","男"}, 1}
s.Eat()
s.Walk()
}
多态/interface
使用interface 实现多态
不同于一般oop的向上转型,GO通过接口机制实现多态(很新奇啊)
package main
import (
"fmt"
)
// 接口的本质是一个指针,有点类似于cpp的虚指针,指向着当前的类类型和函数表
type AnimalIF interface {
Sleep()
GetColor() string
GetType() string
}
// 具体的类,实现AnimalIF 接口
// 无需特定指名,只要实现了接口的所有函数就默认实现了该接口。。。好奇怪的机制
type Cat struct {
color string
}
func (this *Cat) Sleep() {
fmt.Println("cat sleep")
}
func (this *Cat) GetColor() string {
return this.color
}
func (this *Cat) GetType() string {
return "Cat"
}
// Dog 实现AnimalIF 接口
type Dog struct {
color string
}
func (this *Dog) Sleep() {
fmt.Println("dog sleep")
}
func (this *Dog) GetColor() string {
return this.color
}
func (this *Dog) GetType() string {
return "Dog"
}
func ShowAnimal(animal AnimalIF) {
animal.Sleep()
fmt.Println(animal.GetColor(),animal.GetType())
}
func main() {
var animal AnimalIF // 定义一个所谓的原始对象
animal = &Cat{"Green"} // animal 本质是一个指针
animal.Sleep()
fmt.Println(animal.GetColor(),animal.GetType())
animal = &Dog{"Yellow"} // animal 本质是一个指针
animal.Sleep()
fmt.Println(animal.GetColor(),animal.GetType())
fmt.Println("--------------")
// 上述注释可以抽象出一个函数
ShowAnimal(&Cat{"Red"})
}
interface空接口万能类型和类型断言机制
interface 可以成为通用万能类型(void* 这种) interface{} : 空接口
GO提供的基本的数据类型(包括struct)都实现了interface{}
所以可以用interface{} 这个类型的变量引用所有类型的数据
1.18版本 好像有更好的方式:知道了,1.18以后可以等效于std::any, 无论是指针还是值都可以使用interface{}.
package main
import(
"fmt"
)
type Human struct {
Name string
Sex string
}
func myFunc(arg interface{}) {
fmt.Println("---------------")
fmt.Printf("arg.type = %T, arg.value = %v\n", arg,arg)
// 只有interface{} 类型的变量才能这么用(断言机制)
// 只有当ok 为true 的时候,value 的值才非空,且为该类型的字面量
value, ok := arg.(string)
if !ok {
fmt.Println("arg is not string,value = ",value)
} else {
fmt.Println("arg is string,value = ",value)
}
}
func main() {
myFunc(1)
myFunc(3.14)
myFunc("str")
myFunc(Human{Name:"张三",Sex:"男"})
}
interface{} 的机制: 通过interface{} 的类型断言机制,在底层一个一个判断??
// 举例,对于interface{} 类型的变量arg
value,ok := arg.(string)
if ok {
value == arg.value
type == string
}
else {
value == nil
type != string
}
tips: 语法糖 如果实现了接收者是值类型的方法,会隐含地也实现了接收者是指针类型的方法。
变量的内置pair结构
反射的主要作用: 通过变量找到变量的concrete type(具体类型)
- 变量在底层的结构
在接口的变量赋值的过程中,其对应的pair直接传递,保持不变的
package main
import(
"fmt"
)
func main() {
var a string
// 基本数据类型 都属于 static type
// 此时a的结构应该是一个 pair<statictype:string, value:"abcd">
a = "abcd"
// pair<type:string, value:"abcd">
var allType interface{}
allType = a
value,ok := allType.(string)
fmt.Println(value)
}
package main
import(
"fmt"
)
type Reader interface {
ReadBook()
}
type Writer interface {
WriterBook()
}
type Book struct {
}
func (this *Book) ReadBook() {
fmt.Println("read")
}
func (this *Book) WriterBook() {
fmt.Println("Write")
}
func main() {
// 此时的 pair<type:Book, value:book{}>
a := &Book{}
// var a interface{} = &Book{}
fmt.Printf("type = %T\n",a)
// 此时的 pair<type, value:>
var r Reader
// 此时的 pair<type:Book, value:book{}>
r = a
r.ReadBook()
// r.WriterBook() // r 中没有WirterBook放法,所以就算类型正确也找不到该方法
// 此时的 pair<type:Book, value:book{}> , pair 依旧没有变
var w Writer = r.(Writer) // w 和 r 具体的type一直相同,所以才能断言成功
// w.ReadBook() // w 中没有ReadBook放法,所以就算类型正确也找不到该方法
w.WriterBook()
}