外贸企业 访问国外网站,西安到北京高铁多长时间,如何做视频购物网站,万网注册的域名怎么建设网站文章目录 接口:one: 接口基础:two: 接口类型断言和空接口:star2: 空接口实现存储不同数据类型的切片/数组:star2: 复制切片到空接口切片:star2: 类型断言 反射 #x1f4c5; 2024年5月9日
#x1f4e6; 使用版本为1.21.5
接口
十、Java类的封装和继承、多态 - 七点半的菜市… 文章目录 接口:one: 接口基础:two: 接口类型断言和空接口:star2: 空接口实现存储不同数据类型的切片/数组:star2: 复制切片到空接口切片:star2: 类型断言 反射 2024年5月9日 使用版本为1.21.5
接口
十、Java类的封装和继承、多态 - 七点半的菜市场 (tanc.fun) Java的接口 ps: 我感觉Go的接口和类方法两个就很模糊 1️⃣ 接口基础
⭐️ 接口就是一些未实现功能的集合(我是这样理解的)为了实现多态(就是多状态)
type PersonIF interface {Status() //人类的状态Skills() string //会的技能
}⭐️ 在Go语言中接口可以有值它的值默认是nil接口本质上是一个指针一个类型如果实现了接口中的所有方法那么这个类实现的方法就会使用指针自动指向接口的方法
⚠️ 注意是所有抽象方法如果有一个没实现都会报错
⭐️ 类型不需要使用什么关键字来表面实现了这个接口多个类型可以同时实现一个接口一个类型也可以实现多个接口
type PersonIF interface {Status()Skills()
}type WorkIF interface {Class()Income()
}type Person struct { //人的基本Name stringage intsex string
}/*Person实现PersonIF接口
*/
func (this *Person) Status() {println(Status)
}func (this *Person) Skills() {println(Skills)
}/*Person实现WorkIF接口*/
func (this *Person) Class() {println(Class)
}
func (this *Person) Income() {println(Income)
}
⭐️ 调用则和Java差不多这里可以直接使用Interfer来创建一个变量调用然后就可以调用实现interfer内的方法
type PersonIF interface {Status()Skills()
}type WorkIF interface {Class()Income()
}type Person struct { //人的基本Name stringage intsex string
}/*
Person实现PersonIF接口
*/
func (this *Person) Status() {println(Status)
}func (this *Person) Skills() {println(Skills)
}/*
Person实现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, 男} //创建一个对象pIn : PersonIF(p1) //实现接口pIn.Status() //调用接口的方法接口无法调用类自己的方法也就是无法调用GetTestpIn.Skills()p1.GetTest()fmt.Println(pIn) //接口变量包含了接受实列的值和指向对应方法表的指针这里输出的是实列的值
}⭐️ 接口也可以内嵌接口
type PersonIF interface {Status()Skills()WorkIF
}type WorkIF interface {Class()Income()
}
func main() {p1 : Person{张三, 18, 男} //创建一个对象pIn : PersonIF(p1) //实现接口pIn.Class() //可以调用嵌套接口中的实现fmt.Println(pIn)
}
2️⃣ 接口类型断言和空接口
⭐️ Go语言有个万能的类型通配符是一个空接口interfer{}因为在Go中任何接口类型都实现了空接口类似于Java中的Object
⭐️ 每个空接口变量在内存中占据两个字长一个是用来存储包含的类型一个是存储数据或者指向数据的指针
func main() {Test(1)Test(abc)Test(1.111)
}func Test(i interface{}) { //空类型fmt.Println(i)
}//输出:
1
abc
1.111 空接口实现存储不同数据类型的切片/数组
⭐️ 直接使用type给空接口一个别名类型然后创建这个接口别名类型的切片/数组(真的太聪明了这个办法)
package mainimport fmttype 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) //整数v.Set(1, 222) //stringv.Set(2, 13.14) //float32v.Get(0)v.Get(1)v.Get(2)
}//输出:
22
222
13.14复制切片到空接口切片
⭐️ 如果你需要将切片复制到一个空接口切片中需要通过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)
}⭐️ 一个接口的值是可以赋值给另外一个接口变量只要底层类型实现了必要的方法 类型断言
⭐️ 如果变量是一个接口变量则直接可以使用断言机制直接就是接口变量.(类型)即可注意一定要是接口变量
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(我不想学习\n)}
}反射 Go 语言反射的实现原理 | Go 语言设计与实现 (draveness.me) 可以好好看看 ⭐️ Go语言的反射机制也是通过空接口来实现的在reflect包中
⭐️ 反射提供了两个简单的函数实现一个是TypeOf获取类型信息一个是ValueOf获取数据的运行时表示
它们两个对应了两个不同的接口分别是Type和Value ⭐️ TypeOf返回的是一个Type接口对象所以说它可以使用Type方法
它接受一个any类型的值
type any interface{} //空接口// TypeOf函数返回给定参数i的类型。
//
// 参数:
// i - 任意类型的值。
//
// 返回值:
// Type - 表示参数i类型的类型值。
func TypeOf(i any) Type {// 将i转换为emptyInterface类型以获取其动态类型信息。eface : *(*emptyInterface)(unsafe.Pointer(i))// 使用noescape函数确保i的指针不会逃逸这样做是为了避免i的生命周期被延长。// 这是基于Value.typ字段的注释中提到的安全理由。return toType((*abi.Type)(noescape(unsafe.Pointer(eface.typ))))
}
⭐️ 在Type接口中内定义了很多方法
type Type interface {// 基本属性Align() int // 返回类型值对齐所需的字节数。FieldAlign() int // 返回类型作为结构体字段时的对齐要求。// 方法相关Method(int) Method // 获取方法集合中的第i个方法。NumMethod() int // 返回方法总数。MethodByName(string) (Method, bool) // 通过名称查找方法。// 类型基本信息Name() string // 返回类型名。PkgPath() string // 返回类型所在的包路径。Size() uintptr // 返回类型的大小。String() string // 返回类型表示的字符串。Kind() Kind // 返回类型的基本类别。// 类型兼容性Implements(Type) bool // 检查是否实现某个接口。AssignableTo(Type) bool // 检查值是否可直接赋值给另一类型。ConvertibleTo(Type) bool // 检查值是否可通过类型转换匹配另一类型。Comparable() bool // 指示类型值是否可比较。// 特定类型特有操作Elem() Type // 对数组/切片/指针/映射/通道返回元素类型。ChanDir() ChanDir // 对通道类型返回通信方向。IsVariadic() bool // 对函数类型检查是否可变参数。Key() Type // 对映射类型返回键的类型。Len() int // 对数组类型返回其长度。// 结构体操作Field(int) StructField // 获取结构体的第i个字段信息。NumField() int // 返回结构体字段数量。FieldByIndex([]int) StructField // 通过嵌套索引获取字段信息。FieldByName(string) (StructField, bool) // 通过字段名获取字段信息。FieldByNameFunc(func(string) bool) (StructField, bool) // 通过匹配函数查找字段。// 函数类型操作In(int) Type // 获取函数的第i个输入参数类型。NumIn() int // 返回函数输入参数数量。Out(int) Type // 获取函数的第i个输出参数类型。NumOut() int // 返回函数输出参数数量。// 内部方法用户通常无需直接调用common(), uncommon() *internal // 返回底层实现相关的数据结构。
}⭐️ ValueOf 放回一个Value对象接受的也是一个任意类型的值
// ValueOf 是一个将任意类型转换为 Value 类型的函数。
// 参数 i 为任意类型的值表示需要转换的值。
// 返回值为 Value 类型表示转换后的值。
// 如果输入值 i 为 nil则返回一个空的 Value。
func ValueOf(i any) Value {// 检查输入值是否为 nil如果是则返回空的 Valueif i nil {return Value{}}// 如果满足特定条件go121noForceValueEscape 为 false则标记 i 为逃逸变量if !go121noForceValueEscape {escapes(i)}// 将输入值 i 解包为 Value 类型并返回return unpackEface(i)
}
⭐️ 在Value中也定义了一些方法反射包中 reflect.Value 的类型与 reflect.Type 不同它被声明成了结构体。这个结构体没有对外暴露的字段但是提供了获取或者写入数据的方法
type Value struct {// 内部结构未公开实际包含值和类型等信息
}// 实例化Value的方法通常通过reflect.ValueOf或reflect零值的间接操作获得// Kind 返回此Value所持有的值的类型种类。
func (v Value) Kind() Kind// Type 返回此Value所持有的值的具体类型信息。
func (v Value) Type() Type// Bool 获取bool类型的值如果Value不是bool类型则会panic。
func (v Value) Bool() bool// Int 获取整数值类型必须是整数类型否则会panic。
func (v Value) Int() int64// Float 获取浮点数值类型必须是浮点数类型否则会panic。
func (v Value) Float() float64// String 获取字符串值类型必须是string否则会panic。
func (v Value) String() string// Interface 转换Value为interface{}类型几乎所有类型都可以通过此方法获取。
func (v Value) Interface() interface{}// Set 设置Value的值新值必须是可设置的并且类型匹配。
func (v Value) Set(x Value) // SetBool 设置bool类型的值Value必须是可设置的bool类型。
func (v Value) SetBool(x bool)// SetInt 设置整数值Value必须是可设置的整数类型。
func (v Value) SetInt(x int64)// SetFloat 设置浮点数值Value必须是可设置的浮点数类型。
func (v Value) SetFloat(x float64)// SetString 设置字符串值Value必须是可设置的string类型。
func (v Value) SetString(x string)// Call 对于函数或方法Value执行调用并返回结果。
func (v Value) Call(in []Value) []Value// Elem 如果Value是一个指针返回它指向的值的Value否则返回自身。
func (v Value) Elem() Value// Field 获取结构体Value的第i个字段的Value。
func (v Value) Field(i int) Value// FieldByName 获取结构体Value的名为name的字段的Value。
func (v Value) FieldByName(name string) Value// Index 对于数组、slice或map的Value返回索引i处的元素Value。
func (v Value) Index(i int) Value// MapIndex 对于map的Value返回key对应的元素Value。
func (v Value) MapIndex(key Value) Value// CanSet 返回此Value是否可被设置即是否可以更改其底层值。
func (v Value) CanSet() bool// IsZero 判断Value的底层值是否为零值。
func (v Value) IsZero() bool⭐️ Type方法实列
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 { //放回结构体字段的个数然后输出遍历field : p1_demo.Field(i) //获取字段fmt.Println(field.Name, field.Type, field.Offset)}for i : 0; i p1_demo.NumMethod(); i { //放回结构体方法的个数然后输出遍历method : p1_demo.Method(i) //获取方法fmt.Println(method.Name, method.Type)}//数组p2 : [1]Person{Person{张三, 18, 男}}p2_demo : reflect.TypeOf(p2)fmt.Println(p2_demo.Len()) //输出数组长度}⭐️ Value方法实现可以使用ValueOf方法来修改值
package mainimport (fmtreflect
)type Person struct {Name stringAge intSex string
}func main() {// 使用指针来确保可以通过反射修改值p1 : Person{张三, 18, 男}fmt.Println(p1)// 获取p1的反射值p1_demo : reflect.ValueOf(p1)// 由于p1是指针我们先获取其指向的值的反射值p1_val : p1_demo.Elem()// 检查是否可以设置值if p1_val.CanSet() {// 创建一个新的Person值并通过反射设置newPerson : Person{李四, 20, 女}p1_val.Set(reflect.ValueOf(newPerson))} else {fmt.Println(无法设置值)}// 打印修改后的值fmt.Printf(%#v\n, p1) // 使用%#v格式化输出结构体细节fmt.Println(p1_demo.Kind()) // 输出类型信息
}
欢迎关注我继续探讨技术如果觉得写的不错动动小手点个小赞如果觉得我还有哪些不足可以私信哦