移動(dòng)網(wǎng)站的開(kāi)發(fā)流程圖搜索引擎培訓(xùn)班
文章目錄
- 接口
- :one: 接口基礎(chǔ)
- :two: 接口類型斷言和空接口
- :star2: 空接口實(shí)現(xiàn)存儲(chǔ)不同數(shù)據(jù)類型的切片/數(shù)組
- :star2: 復(fù)制切片到空接口切片
- :star2: 類型斷言
- 反射
📅 2024年5月9日
📦 使用版本為1.21.5
接口
十、Java類的封裝和繼承、多態(tài) - 七點(diǎn)半的菜市場(chǎng) (tanc.fun) Java的接口
ps: 我感覺(jué)Go的接口和類方法兩個(gè)就很模糊
1?? 接口基礎(chǔ)
?? 接口就是一些未實(shí)現(xiàn)功能的集合(我是這樣理解的),為了實(shí)現(xiàn)多態(tài)(就是多狀態(tài))
type PersonIF interface {Status() //人類的狀態(tài)Skills() string //會(huì)的技能
}
?? 在Go
語(yǔ)言中接口可以有值,它的值默認(rèn)是nil
,接口本質(zhì)上是一個(gè)指針,一個(gè)類型如果實(shí)現(xiàn)了接口中的所有方法,那么這個(gè)類實(shí)現(xiàn)的方法就會(huì)使用指針自動(dòng)指向接口的方法
?? 注意是所有抽象方法,如果有一個(gè)沒(méi)實(shí)現(xiàn)都會(huì)報(bào)錯(cuò)
?? 類型不需要使用什么關(guān)鍵字來(lái)表面實(shí)現(xiàn)了這個(gè)接口,多個(gè)類型可以同時(shí)實(shí)現(xiàn)一個(gè)接口,一個(gè)類型也可以實(shí)現(xiàn)多個(gè)接口
type PersonIF interface {Status()Skills()
}type WorkIF interface {Class()Income()
}type Person struct { //人的基本Name stringage intsex string
}/*Person實(shí)現(xiàn)PersonIF接口
*/
func (this *Person) Status() {println("Status")
}func (this *Person) Skills() {println("Skills")
}/*Person實(shí)現(xiàn)WorkIF接口*/
func (this *Person) Class() {println("Class")
}
func (this *Person) Income() {println("Income")
}
?? 調(diào)用則和Java
差不多,這里可以直接使用Interfer
來(lái)創(chuàng)建一個(gè)變量調(diào)用,然后就可以調(diào)用實(shí)現(xiàn)interfer
內(nèi)的方法
type PersonIF interface {Status()Skills()
}type WorkIF interface {Class()Income()
}type Person struct { //人的基本Name stringage intsex string
}/*
Person實(shí)現(xiàn)PersonIF接口
*/
func (this *Person) Status() {println("Status")
}func (this *Person) Skills() {println("Skills")
}/*
Person實(shí)現(xiàn)WorkIF接口
*/
func (this *Person) Class() {println("Class")
}
func (this *Person) Income() {println("Income")
}//Person類自己的方法
func (this *Person) GetTest() {println("GetTest")
}func main() {p1 := Person{"張三", 18, "男"} //創(chuàng)建一個(gè)對(duì)象pIn := PersonIF(&p1) //實(shí)現(xiàn)接口pIn.Status() //調(diào)用接口的方法,接口無(wú)法調(diào)用類自己的方法,也就是無(wú)法調(diào)用GetTestpIn.Skills()p1.GetTest()fmt.Println(pIn) //接口變量包含了接受實(shí)列的值和指向?qū)?yīng)方法表的指針,這里輸出的是實(shí)列的值
}
?? 接口也可以內(nèi)嵌接口
type PersonIF interface {Status()Skills()WorkIF
}type WorkIF interface {Class()Income()
}
func main() {p1 := Person{"張三", 18, "男"} //創(chuàng)建一個(gè)對(duì)象pIn := PersonIF(&p1) //實(shí)現(xiàn)接口pIn.Class() //可以調(diào)用嵌套接口中的實(shí)現(xiàn)fmt.Println(pIn)
}
2?? 接口類型斷言和空接口
?? Go語(yǔ)言有個(gè)萬(wàn)能的類型通配符,是一個(gè)空接口interfer{}
,因?yàn)樵?code>Go中任何接口類型都實(shí)現(xiàn)了空接口,類似于Java
中的Object
?? 每個(gè)空接口變量在內(nèi)存中占據(jù)兩個(gè)字長(zhǎng),一個(gè)是用來(lái)存儲(chǔ)包含的類型,一個(gè)是存儲(chǔ)數(shù)據(jù)或者指向數(shù)據(jù)的指針
func main() {Test(1)Test("abc")Test(1.111)
}func Test(i interface{}) { //空類型fmt.Println(i)
}//輸出:
1
abc
1.111
🌟 空接口實(shí)現(xiàn)存儲(chǔ)不同數(shù)據(jù)類型的切片/數(shù)組
?? 直接使用type
給空接口一個(gè)別名類型,然后創(chuàng)建這個(gè)接口別名類型的切片/數(shù)組(真的太聰明了這個(gè)辦法)
package mainimport "fmt"type Empty interface{}type Vector struct {e []Empty
}func (this *Vector) NewInit() { //初始化切片this.e = make([]Empty, 5)
}func (this *Vector) Set(i int, a interface{}) { //將元素插入指定索引this.e[i] = a
}
func (this *Vector) Get(i int) { //獲取指定索引元素并輸出fmt.Println(this.e[i])
}func main() {var v Vectorv.NewInit() //初始化v.Set(0, 22) //整數(shù)v.Set(1, "222") //stringv.Set(2, 13.14) //float32v.Get(0)v.Get(1)v.Get(2)
}//輸出:
22
222
13.14
🌟 復(fù)制切片到空接口切片
?? 如果你需要將切片復(fù)制到一個(gè)空接口切片中需要通過(guò)for-range
,不能直接傳遞
func main() {dataSlice := make([]int, 0)dataSlice = append(dataSlice, 1, 2, 3, 4, 5, 6)emptySlice := make([]interface{}, 10)for i, j := range dataSlice {emptySlice[i] = j}fmt.Println(emptySlice)
}
?? 一個(gè)接口的值是可以賦值給另外一個(gè)接口變量,只要底層類型實(shí)現(xiàn)了必要的方法
🌟 類型斷言
?? 如果變量是一個(gè)接口變量,則直接可以使用斷言機(jī)制,直接就是接口變量.(類型)
即可,注意一定要是接口變量
func main() {Test(1)Test("abc")
}func Test(i interface{}) { //空類型if value, ok := i.(string); ok {fmt.Printf("值: %v 是string\n", value)} else {fmt.Printf("值: %v 不是string\n", i)}
}
?? 還有一種是type-switch
func main() {Test(1)Test("abc")
}func Test(i interface{}) { //空類型switch v := i.(type) {case string:fmt.Printf("值 %v 是string\n", v)case int:fmt.Printf("值 %v 是int\n", v)case float32:fmt.Printf("值 %v 是float32\n", v)default:fmt.Printf("我不想學(xué)習(xí)\n")}
}
反射
Go 語(yǔ)言反射的實(shí)現(xiàn)原理 | Go 語(yǔ)言設(shè)計(jì)與實(shí)現(xiàn) (draveness.me) 可以好好看看
?? Go
語(yǔ)言的反射機(jī)制也是通過(guò)空接口來(lái)實(shí)現(xiàn)的,在reflect
包中
?? 反射提供了兩個(gè)簡(jiǎn)單的函數(shù)實(shí)現(xiàn)一個(gè)是TypeOf
(獲取類型信息),一個(gè)是ValueOf
(獲取數(shù)據(jù)的運(yùn)行時(shí)表示)
它們兩個(gè)對(duì)應(yīng)了兩個(gè)不同的接口分別是Type
和Value
?? TypeOf
返回的是一個(gè)Type
接口對(duì)象,所以說(shuō)它可以使用Type
方法
它接受一個(gè)any
類型的值
type any = interface{} //空接口
// TypeOf函數(shù)返回給定參數(shù)i的類型。
//
// 參數(shù):
// i - 任意類型的值。
//
// 返回值:
// Type - 表示參數(shù)i類型的類型值。
func TypeOf(i any) Type {// 將i轉(zhuǎn)換為emptyInterface類型,以獲取其動(dòng)態(tài)類型信息。eface := *(*emptyInterface)(unsafe.Pointer(&i))// 使用noescape函數(shù)確保i的指針不會(huì)逃逸,這樣做是為了避免i的生命周期被延長(zhǎng)。// 這是基于Value.typ字段的注釋中提到的安全理由。return toType((*abi.Type)(noescape(unsafe.Pointer(eface.typ))))
}
?? 在Type
接口中內(nèi)定義了很多方法
type Type interface {// 基本屬性Align() int // 返回類型值對(duì)齊所需的字節(jié)數(shù)。FieldAlign() int // 返回類型作為結(jié)構(gòu)體字段時(shí)的對(duì)齊要求。// 方法相關(guān)Method(int) Method // 獲取方法集合中的第i個(gè)方法。NumMethod() int // 返回方法總數(shù)。MethodByName(string) (Method, bool) // 通過(guò)名稱查找方法。// 類型基本信息Name() string // 返回類型名。PkgPath() string // 返回類型所在的包路徑。Size() uintptr // 返回類型的大小。String() string // 返回類型表示的字符串。Kind() Kind // 返回類型的基本類別。// 類型兼容性Implements(Type) bool // 檢查是否實(shí)現(xiàn)某個(gè)接口。AssignableTo(Type) bool // 檢查值是否可直接賦值給另一類型。ConvertibleTo(Type) bool // 檢查值是否可通過(guò)類型轉(zhuǎn)換匹配另一類型。Comparable() bool // 指示類型值是否可比較。// 特定類型特有操作Elem() Type // 對(duì)數(shù)組/切片/指針/映射/通道,返回元素類型。ChanDir() ChanDir // 對(duì)通道類型,返回通信方向。IsVariadic() bool // 對(duì)函數(shù)類型,檢查是否可變參數(shù)。Key() Type // 對(duì)映射類型,返回鍵的類型。Len() int // 對(duì)數(shù)組類型,返回其長(zhǎng)度。// 結(jié)構(gòu)體操作Field(int) StructField // 獲取結(jié)構(gòu)體的第i個(gè)字段信息。NumField() int // 返回結(jié)構(gòu)體字段數(shù)量。FieldByIndex([]int) StructField // 通過(guò)嵌套索引獲取字段信息。FieldByName(string) (StructField, bool) // 通過(guò)字段名獲取字段信息。FieldByNameFunc(func(string) bool) (StructField, bool) // 通過(guò)匹配函數(shù)查找字段。// 函數(shù)類型操作In(int) Type // 獲取函數(shù)的第i個(gè)輸入?yún)?shù)類型。NumIn() int // 返回函數(shù)輸入?yún)?shù)數(shù)量。Out(int) Type // 獲取函數(shù)的第i個(gè)輸出參數(shù)類型。NumOut() int // 返回函數(shù)輸出參數(shù)數(shù)量。// 內(nèi)部方法,用戶通常無(wú)需直接調(diào)用common(), uncommon() *internal // 返回底層實(shí)現(xiàn)相關(guān)的數(shù)據(jù)結(jié)構(gòu)。
}
?? ValueOf
放回一個(gè)Value
對(duì)象,接受的也是一個(gè)任意類型的值
// ValueOf 是一個(gè)將任意類型轉(zhuǎn)換為 Value 類型的函數(shù)。
// 參數(shù) i 為任意類型的值,表示需要轉(zhuǎn)換的值。
// 返回值為 Value 類型,表示轉(zhuǎn)換后的值。
// 如果輸入值 i 為 nil,則返回一個(gè)空的 Value。
func ValueOf(i any) Value {// 檢查輸入值是否為 nil,如果是則返回空的 Valueif i == nil {return Value{}}// 如果滿足特定條件(go121noForceValueEscape 為 false),則標(biāo)記 i 為逃逸變量if !go121noForceValueEscape {escapes(i)}// 將輸入值 i 解包為 Value 類型并返回return unpackEface(i)
}
?? 在Value
中也定義了一些方法,反射包中 reflect.Value
的類型與 reflect.Type
不同,它被聲明成了結(jié)構(gòu)體。這個(gè)結(jié)構(gòu)體沒(méi)有對(duì)外暴露的字段,但是提供了獲取或者寫(xiě)入數(shù)據(jù)的方法
type Value struct {// 內(nèi)部結(jié)構(gòu)未公開(kāi),實(shí)際包含值和類型等信息
}// 實(shí)例化Value的方法通常通過(guò)reflect.ValueOf或reflect零值的間接操作獲得// Kind 返回此Value所持有的值的類型種類。
func (v Value) Kind() Kind// Type 返回此Value所持有的值的具體類型信息。
func (v Value) Type() Type// Bool 獲取bool類型的值,如果Value不是bool類型則會(huì)panic。
func (v Value) Bool() bool// Int 獲取整數(shù)值,類型必須是整數(shù)類型,否則會(huì)panic。
func (v Value) Int() int64// Float 獲取浮點(diǎn)數(shù)值,類型必須是浮點(diǎn)數(shù)類型,否則會(huì)panic。
func (v Value) Float() float64// String 獲取字符串值,類型必須是string,否則會(huì)panic。
func (v Value) String() string// Interface 轉(zhuǎn)換Value為interface{}類型,幾乎所有類型都可以通過(guò)此方法獲取。
func (v Value) Interface() interface{}// Set 設(shè)置Value的值,新值必須是可設(shè)置的并且類型匹配。
func (v Value) Set(x Value) // SetBool 設(shè)置bool類型的值,Value必須是可設(shè)置的bool類型。
func (v Value) SetBool(x bool)// SetInt 設(shè)置整數(shù)值,Value必須是可設(shè)置的整數(shù)類型。
func (v Value) SetInt(x int64)// SetFloat 設(shè)置浮點(diǎn)數(shù)值,Value必須是可設(shè)置的浮點(diǎn)數(shù)類型。
func (v Value) SetFloat(x float64)// SetString 設(shè)置字符串值,Value必須是可設(shè)置的string類型。
func (v Value) SetString(x string)// Call 對(duì)于函數(shù)或方法Value,執(zhí)行調(diào)用并返回結(jié)果。
func (v Value) Call(in []Value) []Value// Elem 如果Value是一個(gè)指針,返回它指向的值的Value;否則返回自身。
func (v Value) Elem() Value// Field 獲取結(jié)構(gòu)體Value的第i個(gè)字段的Value。
func (v Value) Field(i int) Value// FieldByName 獲取結(jié)構(gòu)體Value的名為name的字段的Value。
func (v Value) FieldByName(name string) Value// Index 對(duì)于數(shù)組、slice或map的Value,返回索引i處的元素Value。
func (v Value) Index(i int) Value// MapIndex 對(duì)于map的Value,返回key對(duì)應(yīng)的元素Value。
func (v Value) MapIndex(key Value) Value// CanSet 返回此Value是否可被設(shè)置,即是否可以更改其底層值。
func (v Value) CanSet() bool// IsZero 判斷Value的底層值是否為零值。
func (v Value) IsZero() bool
?? Type
方法實(shí)列
type Person struct {Name stringAge intSex string
}func (this *Person) toString() string {return fmt.Sprintf("Nmae: %v,Age: %v,Sex: %v", this.Name, this.Age, this.Sex)
}func main() {p1 := Person{"張三", 18, "男"}p1_demo := reflect.TypeOf(p1) //獲取類型信息fmt.Println(p1_demo.Name()) //輸出類名fmt.Println(p1_demo.Size()) //輸出類型大小for i := 0; i < p1_demo.NumField(); i++ { //放回結(jié)構(gòu)體字段的個(gè)數(shù),然后輸出遍歷field := p1_demo.Field(i) //獲取字段fmt.Println(field.Name, field.Type, field.Offset)}for i := 0; i < p1_demo.NumMethod(); i++ { //放回結(jié)構(gòu)體方法的個(gè)數(shù),然后輸出遍歷method := p1_demo.Method(i) //獲取方法fmt.Println(method.Name, method.Type)}//數(shù)組p2 := [1]Person{Person{"張三", 18, "男"}}p2_demo := reflect.TypeOf(p2)fmt.Println(p2_demo.Len()) //輸出數(shù)組長(zhǎng)度}
?? Value
方法實(shí)現(xiàn),可以使用ValueOf
方法來(lái)修改值
package mainimport ("fmt""reflect"
)type Person struct {Name stringAge intSex string
}func main() {// 使用指針來(lái)確保可以通過(guò)反射修改值p1 := Person{"張三", 18, "男"}fmt.Println(p1)// 獲取p1的反射值p1_demo := reflect.ValueOf(&p1)// 由于p1是指針,我們先獲取其指向的值的反射值p1_val := p1_demo.Elem()// 檢查是否可以設(shè)置值if p1_val.CanSet() {// 創(chuàng)建一個(gè)新的Person值并通過(guò)反射設(shè)置newPerson := Person{"李四", 20, "女"}p1_val.Set(reflect.ValueOf(newPerson))} else {fmt.Println("無(wú)法設(shè)置值")}// 打印修改后的值fmt.Printf("%#v\n", p1) // 使用%#v格式化輸出結(jié)構(gòu)體細(xì)節(jié)fmt.Println(p1_demo.Kind()) // 輸出類型信息
}
歡迎關(guān)注我,繼續(xù)探討技術(shù),如果覺(jué)得寫(xiě)的不錯(cuò)動(dòng)動(dòng)小手點(diǎn)個(gè)小贊,如果覺(jué)得我還有哪些不足可以私信哦