Golang接口{}类型误解

时间:2016-08-08 13:02:27

标签: json pointers go interface unmarshalling

当使用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}

现在我没有谷歌搜索查询。

1 个答案:

答案 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拿着一个指针时,只需传递它。