Go类型特性
- 静态类型:编译确定无法改变
- 类型推断::=
- 类型安全:两个类型不能直接赋值,需要类型类型断言或者转换
- 丰富的基本类型
- 强大的符合类型
- 指针
- 接口类型
- 类型转换
- 泛型: go 1.18版本以后
经典 i != j
type MyInt int
var i int = 0
var j MyInt = 1
j = i
i = j
interface
interface类型(强调一下:是个类型)可以存储任何具体类型,比如:int,int32,int64, struct etc.
interface{}
这个一个空interface,这表示什么?interface <=> any。inteface{}是代表能满足任何值,因为每个值都有一个或者多个方法。
一个interface表示什么?
Go中的接口类型变量存储两个部分:变量的具体值和值的具体类型描述符。
Go反射法则
- 反射从接口值到反射对象(Reflection goes from interface value to reflection object.)。
- 反射从反射对象到反射值。
- 要修改反射对象,那么这个值必须是可设置的(settable)
反射从接口值到反射对象
这条法则表示要从街口值获取反射对象。要使用反射来操作就得先获取这个值的接口对象。
在go中,接口值由两部分组成:类型信息和值信息。反射对象包含了接口值的类型信息和值信息,还有一写方法和函数查询和操作这些信息。
反射基础:
- reflect.Typ
- reflect.Value
编码来试下:
var x int32 = 4
fmt.Println("type:", reflect.TypeOf(x))
反射过程可以从反向对象到接口值
这个法则是说:使用reflect.ValueOf函数可以获取到一个反射对象,然后可以通过反射对象的方法将其转换为具体的值。
举个例子:
func TestReflect_Law2(t *testing.T) {
var x int = 10
// 获取反射值对象
v := reflect.ValueOf(x)
if v.Kind() == reflect.Int {
// 将值对象转换为interface
interfaceValue := v.Interface()
// 使用go的类型断言转换为具体类型的值
value := interfaceValue.(int)
// 像普通值一样传递给其他函数或者方法
// 输出: Value: 10
fmt.Println("Value:", value)
}
}
要修改反射对象,那么这个值必须是可设置的(settable)
可设置需要满足的条件
- 值是指针,并且指向一个可修改的对象
- 值是可修改的
基本上就是要修改反射对象,那么这个对象对应值的描述得是指针,不能是常量有const描述符。
这个遵循Go中参数传递规则:你给函数传一个指针那这个指针在函数内被修改了可以传出;如果你传入一个值,那么在函数内修改了无法传出,因为是值是被复制传入了函数。
可以修改的举例:
func TestReflect_Law3_ShouldSettable_WhenSatisfyConditions(t *testing.T) {
// 条件2: 变量而非常量
x := 10
// 条件1:指针
v := reflect.ValueOf(&x)
// 获取指针指向的值的反射对象
v = v.Elem()
// 修改值为 20
if v.CanSet() {
v.SetInt(20)
// 输出: x: 20
fmt.Println("x:", x)
}
assert.Equal(t, 20, x)
}
不可以修改的举例:
func TestReflect_Law3_ShouldNotSettable_WhenValueIsConstVariable(t *testing.T) {
// 违反条件2:变量而不是常量,这里定义了常量
const x int = 10
// 违反条件2:指针,这里是值
v := reflect.ValueOf(x)
if v.CanSet() {
// 不会执行到这里,因为常量是不可修改的
fmt.Println("x is settable")
// 修改值为 20
v.SetInt(20)
}
assert.Equal(t, 10, x)
}
func TestReflect_Law3_ShouldNotSettable_WhenValueIsNotPointer(t *testing.T) {
// 满足条件2:变量而非常量
x := 10
// 违反条件1:指针,这里是值
v := reflect.ValueOf(x)
if v.CanSet() {
// 不会执行到这里,因为非指针类型的值是不可修改的
fmt.Println("x is settable")
// 修改值为 20
v.SetInt(20)
}
assert.Equal(t, 10, x)
}
三个法则说明了啥?
第一法则表明,可以从任何接口值获取三样东西中的任意一个或全部:接口值的类型(通过 reflect.TypeOf)、接口值的种类(通过 reflect.Value.Kind())和接口值本身(通过 reflect.ValueOf)。
根据第二法则,你可以通过反射对象(reflect.Value)将其转换回接口值(interface{}),这是通过调用 reflect.Value 的 Interface() 方法完成的。之后,如果你知道原始类型,你可以将 interface{} 类型通过类型断言转换回具体类型。
第三法则涉及修改变量。只有那些“可设置(settable)”的 reflect.Value 可以被改变。在反射的上下文中,"可设置" 指的是 reflect.Value 对象是否可以修改对应的值。
反射应用场景?
新建结构对象/示例
reflect.New():
举个例子
场景:你要开发一个代理,你提供了几个内置的负载均衡算法:Roundrobin,Random Weight,它们的负载均衡缩写分别为:rr, weight。用户在配置代理时可以通过rr
和weight
字符串来指定负载均衡算法, 比如:
# Roundrobin 负载均衡
proxy:
uri: /foo/**
upstreams:
lb: rr
targets:
- http://app1:8080
- http://app2:8080
- http://app3:8080
# Weight 负载均衡
proxy:
uri: /foo/**
upstreams:
lb: rr
targets:
- host: http://app1:8080
weight: 10
- host: http://app1:8080
weight: 10
- host: http://app1:8080
weight: 10
然后你定义了一个interface和struct来实现这两个负载均衡算法
type Target struct {
Target string
Weight int
}
// 定义一个负载均衡器接口类型
type Balancer interface {
Elect(hosts []*Target) (*Target, error)
}
// Roundrobin实现
type RoundrobinBalancer struct {}
....
// random weight 实现
type WeightBalancer struct {}
...
使用reflect来创建负载均衡器:
var(
lbRegistry = make(map[string]reflect.Type)
)
// 使用init方法来初始化
func init() {
lbRegistry['rr'] = reflect.TypeOf(RoundrobinBalancer{})
lbRegistry['weight'] = reflect.TypeOf(WeightBalancer{})
}
// 根据lbRegistry创建负载均衡
func New(blance string) (Balancer, error) {
alg, ok := lbRegistry[balance]
if !ok {
return nil, errors.New("Unsupported load balancer algorithm")
}
return reflect.New(alg).Elem().Addr().interface().(Balancer), nil
}