是否可以更改接口定义的指针类型和变量值?
我可以使用反射更改指针值:v.Elem().Set(reflect.ValueOf(&Greeter{"Jack"}).Elem())等效于a = &Greeter{"Jack"}。
v.Elem().Set(reflect.ValueOf(&Greeter{"Jack"}).Elem())
a = &Greeter{"Jack"}
但是我怎样才能使反射等效a = &Greeter2{"Jack"}呢?
a = &Greeter2{"Jack"}
更新:不幸的是,在我要解决的实际问题中,我无法解决原始变量(panic: reflect: reflect.Value.Set using unaddressable value),这就是为什么我要尝试在指针级别进行变通。
panic: reflect: reflect.Value.Set using unaddressable value
这是完整的示例代码:
package main import ( "fmt" "reflect" ) type Greeter struct { Name string } func (g *Greeter) String() string { return "Hello, My name is " + g.Name } type Greeter2 struct { Name string } func (g *Greeter2) String() string { return "Hello, My name is " + g.Name } func main() { var a fmt.Stringer a = &Greeter{"John"} v := reflect.ValueOf(a) v.Elem().Set(reflect.ValueOf(&Greeter{"Jack"}).Elem()) //v.Elem().Set(reflect.ValueOf(&Greeter2{"Jack"}).Elem()) // panic: reflect.Set: value of type main.Greeter2 is not assignable to type main.Greeter fmt.Println(a.String()) // Hello, My name is Jack a = &Greeter2{"Ron"} fmt.Println(a.String()) // Hello, My name is Ron }
Go中的所有内容都是通过价值传递的。接口也。当您传递接口类型的值时,将创建接口值的副本(及其(value;type)内部),并且您将只能修改该副本,而不会影响原始副本。
(value;type)
您具有a接口类型的变量。如果要修改存储在此变量中的值,则必须传递/使用此变量的地址。
a
首先,让我们修改Greeter2.String()方法以知道哪个被调用:
Greeter2.String()
func (g *Greeter2) String() string { return "Hello2, My name is " + g.Name }
并a.String()在首次初始化后调用,以查看它最初确实包含一个*Greeter值,因为我们将很快对其进行更改。
a.String()
*Greeter
var a fmt.Stringer a = &Greeter{"John"} fmt.Println(a.String()) // Hello, My name is John v := reflect.ValueOf(&a).Elem() v.Set(reflect.ValueOf(&Greeter2{"Jack"})) fmt.Println(a.String()) // Hello2, My name is Jack a = &Greeter2{"Ron"} fmt.Println(a.String()) // Hello2, My name is Ron
输出(在Go Playground上尝试):
Hello, My name is John Hello2, My name is Jack Hello2, My name is Ron
因此,本质上没有更改接口 值 的值和类型的事情,仅更改了可能属于接口类型的变量(具有内存地址)中的数据。
所以关键的事情是:
reflect.ValueOf(&a)
*Greeter2
fmt.String
Greeter
有关更多详细信息和深入的介绍,请阅读:The Go Blog:反射法则
编辑:关于修改副本
我的回答的第一部分:每当您传递某些内容时,都会制作一份副本。这还包括将接口值传递给reflect.ValueOf():还将接收一个副本,因此您无法避免这种情况。进行这项工作的唯一方法是传递一个 指针 。因为指针也将被复制,但我们正在修改的 尖 值(不是指针),这是一样的。
reflect.ValueOf()