类型不确定

在不确定类型需要反射的时候,DeepEqual是我们不可不用的强大工具。

比如:

1
2
3
4
5
6
7
func main(){
    m1:=map[string]interface{}{"a":"1", "b":2, "c":3};
    m2:=map[string]interface{}{"a":1, "c":"3", "b":2};

    fmt.Println(`reflect.DeepEqual(m1["a"],m2["a"]`,reflect.DeepEqual(m1["a"],m2["a"]));
    fmt.Println(`reflect.DeepEqual(m1["b"],m2["b"]`,reflect.DeepEqual(m1["b"],m2["b"]));
}

执行结果:

1
2
3
4
Running...

reflect.DeepEqual(m1["a"],m2["a"] false
reflect.DeepEqual(m1["b"],m2["b"] true

这种情况,如果我们自己写代码比较,势必要使用switch type语法,实在是太麻烦了,感谢go包含了这么好的工具。

interface{}的值为指针

当interface的动态类型是指针的时候,且其动态值不为 nil 时,我们可以理解为其结构如下图所示。

这里为什么需要特别拿出来说明呢?因为动态类型为指针的interface的动态值保存的就是一个指针值,这个指针指向一块内存。下面以Golang的error接口来说明这个问题。

下面是error包的代码:

1
2
3
4
5
6
7
8
9
type error interface {
    Error() string
}

func New(text string) error { return &errorString{text} }

type errorString struct { text string }

func (e *errorString) Error() string { return e.text }

这里需要理解的:是指针类型 *errorString 实现了error接口,而不是 errorString 。

1
2
3
w1 := errors.New("ERR")
w2 := errors.New("ERR")
fmt.Println(w1 == w2) // 输出false

以 w1 为例子, 由于是指针类型 *errorString 实现了error接口,所以 w1 的动态类型是 *errorString,那么 w1 的动态值就是一个指针,w2 也是同理。那么上面的等于(==)比较我们可以用下图和伪代码来理解。

1
w1.type == w2.type && w1.value == w2.value

由于 w1.value 和 w2.value 都是指针类型,它们又分别保存着不同的内存地址,所以他们的比较是得出 false

也正是这种实现,每个New函数的调用都分配了一个独特的和其他错误不相同的实例,这能方便的让我们可以定义自己特定的错误,就如同Golang定义的 io.EOF 一样,不必担心刚好有相同的错误消息:

1
fmt.Println(errors.New("EOF") == io.EOF)  //输出false

如果想要让w1 := errors.New("ERR")w2 := errors.New("ERR")比较指针指向的真实值呢?需要使用reflect.DeepEqual()函数

1
2
3
	w1 := errors.New("ERR")
	w2 := errors.New("ERR")
	fmt.Println(reflect.DeepEqual(w1,w2))

结果返回true