golang反射入门一例

Go是一个强类型的静态编程语言。然而,一些Go的特性让它看起来又像是一门动态语言。例如,如果你不确定你接收的参数的类型,你可以使用interface来接收所有类型的参数传递。
记住只有interface是有reflect属性的。

我们注意到interface允许Go实现多态。没有任何一种类型是特别需要强调的。可以是string int64 float32 甚至是集合(array/map)。但计算机运行这些代码时候,reflect帮助检查,修改其自身的结构与行为。这个过程允许我们知道对象的类型以及内存在运行时的结构。

我们为什么需要reflect?

允许提前定义参数类型(通常发生在暴露的API上)

函数能根据传参动态执行

reflect的缺点

  • 影响代码可读性
  • 屏蔽了代码编译时的错误检查。作为动态语言,Go的编译器可以提前检测数据类型的错误,在编译的时候。当数据在interface中没有特性指明类型的时候,服务器会有在运行代码时候出现panic的风险
  • 降低了整体的性能。使用reflect需要服务端去做额外的工作去获取参数的值以及具体的类型,因此,尽量避免在一些很重要的参数上使用interface

经典的例子如下:

1
2
3
var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.1) // Error: will panic

服务器在运行这段代码时候会panic,因为v并不是x,而是x的靠背。因此,所有对于v的修改都是禁止的。

所以,我们需要使用指针来解决这个问题

1
2
3
4
5
var x float64 = 3.4
y := reflect.ValueOf(&x)

fmt.Println(“type of y”, y.Type()) // *float64
fmt.Println(“settability of y:”, y.CanSet()) // false

这时候y仍然不能代替x,你可以通过y.Elem()来修改

1
2
3
4
5
z := y.Elem()
z.SetFloat(7.1)

fmt.Println(z.Interface()) // 7.1
fmt.Println(x) // 7.1

复制代码可以注意到指针会对所指向的值一并作出修改,也就是x

reflect的应用

reflect被广泛应用到对象序列化,fmt相关函数,以及ORM等等上。

DeepEqual函数

在测试一个功能的时候,我们往往需要知道两个变量是否完全一致。例如,判断一个slice中所有的元素是否相同或者检查两个map中所有的key对应的value是否相同。这时就需要DeepEqual函数

1
func DeepEqual(x, y interface{}) bool

复制代码DeepEqual接收两个interface的参数。你可以传入任意值,它会返回一个布尔值表示传入的两个参数是否完全相等。

等一下,什么叫做 deeply 相等,看看下面例子

1
2
3
4
5
6
7
8
type FirstInt int
type SecondInt int

func main() {
m := FirstInt(1)
n := SecondInt(1)
fmt.Println(reflect.DeepEqual(m, n)) // false
}

复制代码在上面例子中虽然m,n都是1,但是他们的数据类型是不一样的,一个是FirstIn类型,一个是SecondInt类型。所以它们是不相等的。

总结

Go作为一门静态语言,我们可以非常明确在语言编写的弹性上来说相比于例如Python这样的动态语言来说肯定是有局限性的。但是通过使用reflect也让我们拥有了一部分动态语言的特性,你可以很容易获取参数的类型以及值,在使用它的时候。

摘自:

作者:野生程序元
链接:https://juejin.im/post/5dc03c466fb9a04a9a08655e
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。