当使用interface{}
作为函数参数类型时,我在Go中遇到了一个错误,当给定非指针类型并使用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
文档:
要将JSON解组为接口值,Unmarshal存储其中一个 这些在界面值: [...]
如果我像这样传递一个指向测试函数的指针,它就可以工作:
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
。因为*Test
中有i
,所以没有用。
因此,在所有逻辑中,如果我在调用&
时将i
放回Unmarshal
,则应该再次使用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}
现在我没有谷歌搜索查询。
答案 0 :(得分:4)
interface{}
是任何值和任何类型的包装器。界面示意性地包装(value; type)
对,具体值及其类型。有关详细信息:The Laws of Reflection #The representation of an interface。
json.Unmarshal()
已获取interface{}
类型的值:
func Unmarshal(data []byte, v interface{}) error
因此,如果您已经有interface{}
值(i interface{}
函数的test()
参数),请不要尝试获取其地址,只是按原样传递。
另请注意,对于任何修改存储在interface{}
中的值的包,您需要传递指向它的指针。那么i
中的内容应该是一个指针。因此,正确的方案是将*Test
传递给test()
,将test()
传递给i
传递给json.Unmarshal()
(不带其地址)。
当i
包含*Test
并且您通过&i
时,它会起作用,因为json
包只会取消引用*interface{}
指针,并找到{ {1}}值,包含interface{}
值。它是一个指针,所以它很好:将JSON对象解组为指向的*Test
值。
当Test
包含i
并且您通过Test
时,同样如上所述:&i
被取消引用,因此找到*interface{}
,其中包含非指针:interface{}
。由于Test
包无法解组为非指针值,因此必须创建新值。由于json
函数的传递值是json.Unmarshal()
类型,它告诉*interface{}
包将数据解组为类型json
的值。这意味着interface{}
包可以自由选择要使用的类型。默认情况下,json
包将JSON对象解组为json
值,因此这是创建和使用的(并最终放入您传递的指针指向的值:map[string]interface{}
)。 / p>
总而言之,避免使用指向接口的指针。而是将“指针”放入接口(接口值应该包裹指针)。当你已经有一个&i
拿着一个指针时,只需传递它。