博主介绍:
– 我是了凡,喜欢每日在简书上投稿日更的读书感悟笔名:了_凡。专注于 Go Web 后端,辅学Python、Java、算法、前端等领域。微信公众号【了凡银河系】期待你的关注。未来大家一起加油啊~
基础汇总篇到指针:https://blog.csdn.net/weixin_45765795/article/details/117278889
什么是结构体:https://blog.csdn.net/weixin_45765795/article/details/117548389
函数和方法有什么区别?:https://blog.csdn.net/weixin_45765795/article/details/117629961
从IDE安装到gin框架简讲:https://blog.csdn.net/weixin_45765795/article/details/117257325
Go语言中接口(interface)是一种类型,一种抽象的类型。
interface是一组方法的集合,是看编程的一种体现,具体接口的功能就是在做一件事情的时候去定义一个协议或者说一种规则。
比如说数豆子,我不管怎么数,我只知道数豆子需要一个一个的数是一个重复在做的动作,那么我就定义一个方法有数豆子这个功能就好了,我不会去关心到底什么姿势数,什么方法数,只关心需要做什么事怎么去做找到重复行为去模仿下来我就可以制作一台智能数豆子机器。
我们都知道语言呢有面向过程的,有面向对象的,但是Go是即面向过程又面向对象的,所以他需要去做一些面向过程做不了的不方便做的事情,例如:
当需要模拟猫和狗的时候就要知道猫和狗都有什么功能
我们可以模拟猫和狗的功能之一就是叫声
猫:喵喵的叫声
狗:汪汪的叫声
看代码演示:
package main
import (
"fmt"
)
type Cat struct{}
func (c Cat) Say() string { return "喵喵喵" }
type Dog struct{}
func (d Dog) Say() string { return "汪汪汪" }
func main() {
c := Cat{}
fmt.Println("猫:", c.Say())
d := Dog{}
fmt.Println("狗:", d.Say())
}
像这样的情况也会有更多需要接口的地方
例如:
三角形,四边形,圆形都能计算周长和面积,我们能不能把它们当成“图形”来处理呢?
销售、行政、程序员都能计算月薪,我们能不能把他们当成“员工”来处理呢?
接口的定义格式:
type 接口类型名 interface{
方法名1( 参数列表1 ) 返回值列表1
方法名2( 参数列表2 ) 返回值列表2
…
}
定义中:
1.接口名:使用type将接口定义为自定义的类型名。Go语言的接口在命名时,一般会在单词后面添加er,如有写操作的接口叫Writer,有字符串功能的接口叫Stringer等。接口名最好要能突出该接口的类型含义。
2.方法名:当方法名首字母是大写且这个接口类型名首字母也是大写时,这个方法可以被接口所在的包(package)之外的代码访问。
3.参数列表、返回值列表:参数列表和返回值列表中的参数变量名可以省略。
代码演示:
type writer interface{
Write([]byte) error
}
当你看到这个接口类型的值时,你不知道它是什么,唯一知道的就是可以通过它的Write方法来做一些事情。
接口重要注意点:
接口是一个或多个方法签名的集合。
任何类型的方法集中只要拥有该接口’对应的全部方法’签名。
就表示它 “实现” 了该接口,无须在该类型上显式声明实现了哪个接口。这称为Structural Typing。
所谓对应方法,是指有相同名称、参数列表 (不包括参数名) 以及返回值。当然,该类型还可以有其他方法。
接口只有方法声明,没有实现,没有数据字段。
接口可以匿名嵌入其他接口,或嵌入到结构中。
对象赋值给接口时,会发生拷贝,而接口内部存储的是指向这个复制品的指针,既无法修改复制品的状态,也无法获取指针。
只有当接口存储的类型和对象都为nil时,接口才等于nil。
接口调用不会做receiver的自动转换。
接口同样支持匿名字段方法。
接口也可实现类似OOP中的多态。
空接口可以作为任何类型数据的容器。
一个类型可实现多个接口。
接口命名习惯以 er 结尾。
一个对象只要全部实现了接口中的方法,那么就实现了这个接口。或者说:接口就是一个需要实现的方法列表。
我们来定义一个Sayer接口:
type Sayer interface { // Sayer 接口
say()
}
定义dog和cat两个结构体:
type dog struct {}
type cat struct {}
Sayer接口里只有一个say方法,我们只需要给dog和cat 分别实现say方法就可以实现Sayer接口了。
// dog实现了Sayer接口
func (d dog) say() {
fmt.Println("汪汪汪")
}
// cat实现了Sayer接口
func (c cat) say() {
fmt.Println("喵喵喵")
}
这样一个接口就实现完成了
反射是指在程序运行期对程序本身进行访问和修改的能力
反射是指在程序运行期对程序本身进行访问和修改的能力。程序在编译时,变量被转换为内存地址,变量名不会被编译器写入到可执行部分。在运行程序时,程序无法获取自身的信息。
支持反射的语言可以在程序编译期将变量的反射信息,如字段名称、类型信息、结构体信息等整合到可执行文件中,并给程序提供接口访问反射信息,这样就可以在程序运行期获取类型的反射信息,并且有能力修改它们。
Go程序在运行期使用reflect包访问程序的反射信息。反射也是在运行时动态的获取一个变量的类型信息和值信息。
首先科普一个小知识:
变量的内在机制
Go语言中的变量是分为两部分的:
类型信息:预先定义好的元信息
值信息:程序运行过程中可动态变化的
Go语言带有一个reflect包
reflect包封装了反射相关的方法
获取类型信息:reflect.TypeOf,是静态的
获取值信息:reflect.ValueOf,是动态的
反射机制中,任何接口值都由是一个具体类型和具体类型的值两部分组成的。
反射的相关功能由内置的reflect包提供,任意接口值在反射中都可以理解为由reflect.Type和reflect.Value两部分组成,并且reflect包提供了reflect.TypeOf和reflect.ValueOf两个函数来获取任意对象的Value和Type。
func TypeOf(i interface{}) Type { // TypeOf返回表示i的动态类型的反射类型。
eface := *(*emptyInterface)(unsafe.Pointer(&i))
return toType(eface.typ) //如果i是一个nil接口值,TypeOf返回nil。
}
func ValueOf(i interface{}) Value { // ValueOf返回一个新值,初始化为存储在接口i中的具体值。ValueOf(nil)返回零值。
if i == nil {
return Value{} // 可能允许Value的内容存在于堆栈上。
}
escapes(i) // escapes 标记值x转义的哑注释,用于反射代码太聪明以至于编译器无法跟踪的情况。
return unpackEface(i)
}
//反射获取interface类型信息
func reflect_type(a interface{}) {
t := reflect.TypeOf(a)
fmt.Println("类型是:", t)
// kind()可以获取具体类型
k := t.Kind()
fmt.Println(k)
switch k {
case reflect.Float64:
fmt.Printf("a is float64\n")
case reflect.String:
fmt.Println("string")
}
}
func main() {
var x float64 = 3.4
reflect_type(x)
}
//反射获取interface值信息
func reflect_value(a interface{}) {
v := reflect.ValueOf(a)
fmt.Println(v)
k := v.Kind()
fmt.Println(k)
switch k {
case reflect.Float64:
fmt.Println("a是:", v.Float())
}
}
func main() {
var x float64 = 3.4
reflect_value(x)
}
// 定义结构体
type User struct {
Id int
Name string
Age int
}
// 修改结构体值
func SetValue(o interface{}) {
v := reflect.ValueOf(o)
// 获取指针指向的元素
v = v.Elem()
// 取字段
f := v.FieldByName("Name")
if f.Kind() == reflect.String {
f.SetString("kuteng")
}
}
func main() {
u := User{1, "5lmh.com", 20}
SetValue(&u)
fmt.Println(u)
}