空接口是绕过类型系统的一种方式吗



我正在学习Go(来自Python(,强制输入系统实际上很有帮助。我对interface{}的理解非常有限,因此我使用了以下代码,从API检索JSON数据并返回解析版本。结果可以是一个对象或对象列表。

func getJsonFromApi(endpoint string) (reply interface{}, err error) {
res, err := http.Get("http://127.0.0.42/api/" + endpoint)
if err != nil {
return nil, err
}
body, err := ioutil.ReadAll(res.Body)
err = res.Body.Close()
if err != nil {
return nil, err
}
err = json.Unmarshal(body, &reply)
return reply, nil
}

它起作用了,但我对颠覆打字系统感到不舒服。这是interface{}的预期用途吗?

我最终会更改代码,使其始终返回一个对象数组(并在第一个用例中获取第一个对象(,但我对一般问题很好奇。

如果可能,最好使用struct。您可以使用map:

package main
import (
"encoding/json"
"net/http"
)
func main() {
r, e := http.Get("https://github.com/manifest.json")
if e != nil {
panic(e)
}
defer r.Body.Close()
m := make(map[string]interface{})
json.NewDecoder(r.Body).Decode(&m)
s := m["icons"].([]interface{})[0].(map[string]interface{})["sizes"].(string)
println(s == "114x114")
}

但正如您所看到的,当您需要打开包装以获得实际价值时,这会非常痛苦。struct要好得多:

package main
import (
"encoding/json"
"net/http"
)
func main() {
r, e := http.Get("https://github.com/manifest.json")
if e != nil {
panic(e)
}
defer r.Body.Close()
var m struct {
Icons []struct { Sizes string }
}
json.NewDecoder(r.Body).Decode(&m)
s := m.Icons[0].Sizes
println(s == "114x114")
}

空接口interface{}是一个指定零方法的接口。它可以保存任何类型的值(https://tour.golang.org/methods/14),所以它是你在不知道实际类型的情况下用来装东西的类型。

在像你这样的例子中,它有时是必要的,但要付出强大的打字代价。我认为在你的例子中使用它不应该感到不舒服,但如果它到处都是,你应该开始感到不舒服。

如果您使用以下签名,则可以允许方法的调用方传入实际类型:

func getJsonFromApi(endpoint string, reply interface{}) (err error) {

我想说这是类型系统的一部分,而不是绕过它的方法

最新更新