reflect

为什么使用反射

有时候我们需要编写一个函数能够处理一类并不满足普通公共接口类型的值,也有可能是因为它们并没有确定的表示方式,或者是在我们设计该函数的时候这些类型可能还不存在等等

比如如下实现一个打印含有String() 方法的自定义类型和基本数据类型比如int、bool等。通过类型推断加上switch case分支进行判断。

func Sprint(x interface{}) string {
	type stringer interface {
		String() string
	}
	switch x := x.(type) {
	case stringer:
		return x.String()
	case string:
		return x
	case int:
		return strconv.Itoa(x)
	case bool:
		if x {
			return "true"
		}
		return "false"
	default:
		return "unknown"
	}
}

写下如下的测试函数,输出结果

type Call struct {
	content string
}

func (c *Call) String() string {
	return c.content
}

func TestSprint(t *testing.T) {
	call := Call{content: "This is call struct"}
	t.Log(Sprint(&call))

	num := 4
	t.Log(Sprint(num))

	bl := true
	t.Log(Sprint(bl))

	str := "a string"
	t.Log(Sprint(str))

	s := struct{}{}
	t.Log(Sprint(s))
}
[admin@localhost whyUseReflect]$ go test -v 
=== RUN   TestSprint
    sprint_test.go:39: This is call struct
    sprint_test.go:42: 4
    sprint_test.go:45: true
    sprint_test.go:48: a string
    sprint_test.go:51: unknown
--- PASS: TestSprint (0.00s)

我们可以无限的添加类型,对每个类型进行添加,输出相应的结果,但是这些类型是无穷的,而且当对自定义类型的某个变量进行检查的时候,需要添加这个变量的定义。且当出现自定义类型本身进行嵌套的时候,会陷入死循环,如下所示:

type repeatCall struct {
    name string
    call *repeatCall
}

此时,就需要引用反射了。