Golang interface{} 类型误解
Golang interface{} type misunderstanding
我在使用 interface{}
作为函数参数类型、给定非指针类型并使用 json.Unmarshal
时遇到了一个错误。
因为一段代码顶一千字,举个例子:
package main
import (
"encoding/json"
"fmt"
)
func test(i interface{}) {
j := []byte(`{ "foo": "bar" }`)
fmt.Printf("%T\n", i)
fmt.Printf("%T\n", &i)
json.Unmarshal(j, &i)
fmt.Printf("%T\n", i)
}
type Test struct {
Foo string
}
func main() {
test(Test{})
}
输出:
main.Test
*interface {}
map[string]interface {}
json.Unmarshal
将我的结构变成 map[string]interface{}
oO...
后面的小阅读解释了其中的一些,interface{}
本身就是一种类型,而不是某种无类型的容器,这解释了*interface{}
,以及json.Unmarshal
可以没有得到初始类型,返回了一个map[string]interface{}
..
来自 Unmarshal
文档:
To unmarshal JSON into an interface value, Unmarshal stores one of
these in the interface value:
[...]
如果我像这样传递一个指向测试函数的指针,它就会工作:
func test(i interface{}) {
j := []byte(`{ "foo": "bar" }`)
fmt.Printf("%T\n", i)
fmt.Printf("%T\n", &i)
json.Unmarshal(j, i)
fmt.Printf("%T\n", i)
fmt.Println(i)
}
func main() {
test(&Test{})
}
输出:
*main.Test
*interface {}
*main.Test
&{bar}
很酷,数据已全部解组,现在在第二个片段中,我在调用 Unmarshal
时删除了 &
。因为我在i
里有一个*Test
,没用。
所以在所有逻辑中,如果我在调用 Unmarshal
时将 &
放回 i
,它应该会再次弄乱 i
的类型。但是没有。
如果我运行:
func test(i interface{}) {
j := []byte(`{ "foo": "bar" }`)
fmt.Printf("%T\n", i)
fmt.Printf("%T\n", &i)
json.Unmarshal(j, &i)
fmt.Printf("%T\n", i)
fmt.Println(i)
}
func main() {
test(&Test{})
}
它仍然有效:
*main.Test
*interface {}
*main.Test
&{bar}
现在我已经没有 google 个搜索查询了。
正确的场景
interface{}
是任何值和任何类型的包装器。一个接口示意性地包装了一个 (value; type)
对,一个具体的值及其类型。有关此的更多详细信息:The Laws of Reflection #The representation of an interface.
json.Unmarshal()
已经采用类型 interface{}
:
的值
func Unmarshal(data []byte, v interface{}) error
所以如果你已经有了一个 interface{}
值(test()
函数的 i interface{}
参数),不要试图获取它的地址,只需将它作为-是。
另请注意,对于任何修改存储在 interface{}
中的值的包,您需要传递一个指向它的指针。所以 i
中应该是一个指针。所以正确的场景是将 *Test
传递给 test()
,并在 test()
内部将 i
传递给 json.Unmarshal()
(不获取其地址)。
其他场景的解释
当 i
包含 *Test
并且您传递 &i
时,它将起作用,因为 json
包将简单地取消引用 *interface{}
指针,并找到一个 interface{}
值,它包含一个 *Test
值。它是一个指针,所以一切都很好:将 JSON 对象解组为指向的 Test
值。
当 i
包含 Test
并且你传递 &i
时,同样的事情发生在上面:*interface{}
被取消引用,所以它找到一个 interface{}
包含一个非指针:Test
。由于 json
包无法解组为非指针值,因此它必须创建一个新值。由于传递给 json.Unmarshal()
函数的值是 *interface{}
类型,它告诉 json
包将数据解组为 interface{}
类型的值。这意味着 json
包可以自由选择要使用的类型。默认情况下,json
包将 JSON 对象解组为 map[string]interface{}
值,因此这就是创建和使用的内容(并最终放入您传递的指针指向的值中:&i
).
总而言之
总而言之,避免使用指向接口的指针。相反 "put" 指向接口的指针(接口值应该包含指针)。当你已经有一个 interface{}
持有一个指针时,只需传递它。
我在使用 interface{}
作为函数参数类型、给定非指针类型并使用 json.Unmarshal
时遇到了一个错误。
因为一段代码顶一千字,举个例子:
package main
import (
"encoding/json"
"fmt"
)
func test(i interface{}) {
j := []byte(`{ "foo": "bar" }`)
fmt.Printf("%T\n", i)
fmt.Printf("%T\n", &i)
json.Unmarshal(j, &i)
fmt.Printf("%T\n", i)
}
type Test struct {
Foo string
}
func main() {
test(Test{})
}
输出:
main.Test
*interface {}
map[string]interface {}
json.Unmarshal
将我的结构变成 map[string]interface{}
oO...
后面的小阅读解释了其中的一些,interface{}
本身就是一种类型,而不是某种无类型的容器,这解释了*interface{}
,以及json.Unmarshal
可以没有得到初始类型,返回了一个map[string]interface{}
..
来自 Unmarshal
文档:
To unmarshal JSON into an interface value, Unmarshal stores one of these in the interface value: [...]
如果我像这样传递一个指向测试函数的指针,它就会工作:
func test(i interface{}) {
j := []byte(`{ "foo": "bar" }`)
fmt.Printf("%T\n", i)
fmt.Printf("%T\n", &i)
json.Unmarshal(j, i)
fmt.Printf("%T\n", i)
fmt.Println(i)
}
func main() {
test(&Test{})
}
输出:
*main.Test
*interface {}
*main.Test
&{bar}
很酷,数据已全部解组,现在在第二个片段中,我在调用 Unmarshal
时删除了 &
。因为我在i
里有一个*Test
,没用。
所以在所有逻辑中,如果我在调用 Unmarshal
时将 &
放回 i
,它应该会再次弄乱 i
的类型。但是没有。
如果我运行:
func test(i interface{}) {
j := []byte(`{ "foo": "bar" }`)
fmt.Printf("%T\n", i)
fmt.Printf("%T\n", &i)
json.Unmarshal(j, &i)
fmt.Printf("%T\n", i)
fmt.Println(i)
}
func main() {
test(&Test{})
}
它仍然有效:
*main.Test
*interface {}
*main.Test
&{bar}
现在我已经没有 google 个搜索查询了。
正确的场景
interface{}
是任何值和任何类型的包装器。一个接口示意性地包装了一个 (value; type)
对,一个具体的值及其类型。有关此的更多详细信息:The Laws of Reflection #The representation of an interface.
json.Unmarshal()
已经采用类型 interface{}
:
func Unmarshal(data []byte, v interface{}) error
所以如果你已经有了一个 interface{}
值(test()
函数的 i interface{}
参数),不要试图获取它的地址,只需将它作为-是。
另请注意,对于任何修改存储在 interface{}
中的值的包,您需要传递一个指向它的指针。所以 i
中应该是一个指针。所以正确的场景是将 *Test
传递给 test()
,并在 test()
内部将 i
传递给 json.Unmarshal()
(不获取其地址)。
其他场景的解释
当 i
包含 *Test
并且您传递 &i
时,它将起作用,因为 json
包将简单地取消引用 *interface{}
指针,并找到一个 interface{}
值,它包含一个 *Test
值。它是一个指针,所以一切都很好:将 JSON 对象解组为指向的 Test
值。
当 i
包含 Test
并且你传递 &i
时,同样的事情发生在上面:*interface{}
被取消引用,所以它找到一个 interface{}
包含一个非指针:Test
。由于 json
包无法解组为非指针值,因此它必须创建一个新值。由于传递给 json.Unmarshal()
函数的值是 *interface{}
类型,它告诉 json
包将数据解组为 interface{}
类型的值。这意味着 json
包可以自由选择要使用的类型。默认情况下,json
包将 JSON 对象解组为 map[string]interface{}
值,因此这就是创建和使用的内容(并最终放入您传递的指针指向的值中:&i
).
总而言之
总而言之,避免使用指向接口的指针。相反 "put" 指向接口的指针(接口值应该包含指针)。当你已经有一个 interface{}
持有一个指针时,只需传递它。