我很难理解泛型和接口是如何相互作用的,也很难知道哪个实现将从定义为返回接口的函数返回。
我怎样才能让下面的代码以我想要的方式工作。
package main
import (
"encoding/json"
"fmt"
"reflect"
)
type IntegrationConfig[T interface{}] interface {
}
type NamedJsonSettings[T interface{}] json.RawMessage
func (m *NamedJsonSettings[T]) GetConfig() (T, error) {
conf := new(T)
err := json.Unmarshal(*m, conf)
return *conf, err
}
type NamedConfig struct {
Username string `json:"username"`
Password string `json:"password"`
}
func getConfig[T interface{}]() IntegrationConfig[T] {
serverConfigStr := `{
"username": "myusername",
"password": "supersecret"
}`
sc := NamedJsonSettings[T](serverConfigStr)
c, err := sc.GetConfig()
if err != nil {
fmt.Printf("ERROR: %s", err)
}
return c
}
func main() {
c := getConfig[NamedConfig]()
fmt.Printf("%+v\n", c)
fmt.Printf("%s\n", c.Username)
fmt.Printf("%s\n", c.Password)
fmt.Printf("%s\n",reflect.TypeOf(c).String())
}
运行此命令将生成以下错误:
./scratch_1.go:43:23: c.Username undefined (type IntegrationConfig[NamedConfig] has no field or method Username)
./scratch_1.go:44:23: c.Password undefined (type IntegrationConfig[NamedConfig] has no field or method Password)
如果我删除这两行,当我在控制台中运行它时,我会看到以下内容,这是我所期望的:
{Username:myusername Password:supersecret}
main.NamedConfig
我哪里做错了?另外,如果您能提供一些很好的资源链接,帮助您更好地理解泛型,特别是在以这种方式使用接口的情况下,我们将不胜感激。
1条答案
按热度按时间9jyewag01#
函数
getConfig[NamedConfig]()
返回IntegrationConfig[NamedConfig]
,我们可以看到它的定义:你可以看到这只是
interface{}
(a.k.a. * 任何值都可以转换为any
,因此实际返回的对象是否具有字段是无关紧要的。我们可以像这样构造一个类似的测试用例:即使
value
中存储的实际值有一个名为field
的字段,类型any
也不会公开该字段。如果要访问字段或方法,这些字段/方法必须按变量的类型公开,而字段/方法是否按值的类型公开并不重要。(这与Python或JavaScript不同,后者允许您访问对象上的任何字段或方法,只要该字段或方法在运行时存在。
如何修复
首先,使用
any
而不是interface{}
是正常的。它们的意思相同,但any
更典型。各种中间类型不是必需的。您可以直接从泛型返回
T
。由于T
是泛型中的普通类型,因此不必使用new(T)
。在这段代码中,变量
c
的类型为NamedConfig
,而不是IntegrationConfig[NamedConfig]
(即any
)。因为它的类型是NamedConfig
--因为 * 变量本身 * 是该类型而不仅仅是值--所以您可以访问这些字段。