vea*_*top 2 reflection pointers interface go
是否可以更改接口定义的变量的指针类型和值?
我可以用反射改变指针值:v.Elem().Set(reflect.ValueOf(&Greeter{"Jack"}).Elem())
这相当于a = &Greeter{"Jack"}
.
但我怎样才能使反射等效于a = &Greeter2{"Jack"}
?
更新:不幸的是,在我试图解决的实际问题中,我无法解决原始变量(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
}
Run Code Online (Sandbox Code Playgroud)
Go 中的一切都是按值传递的。接口也有。当您传递接口类型的值时,将创建接口值的副本(以及(value;type)
内部),并且您只能修改副本,而不会影响原始值。
你有一个变量a
接口类型的变量。如果要修改存储在该变量中的值,则必须传递/使用该变量的地址。
首先让我们修改该Greeter2.String()
方法以了解哪个方法被调用:
func (g *Greeter2) String() string {
return "Hello2, My name is " + g.Name
}
Run Code Online (Sandbox Code Playgroud)
并且a.String()
在第一次 init 之后调用以查看它最初确实包含一个*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
Run Code Online (Sandbox Code Playgroud)
输出(在Go Playground上尝试):
Hello, My name is John
Hello2, My name is Jack
Hello2, My name is Ron
Run Code Online (Sandbox Code Playgroud)
所以本质上不存在改变接口值的值和类型这样的事情仅更改可能属于接口类型的变量(具有内存地址)中的数据。
所以关键是:
reflect.ValueOf(&a)
*Greeter2
实现fmt.String
,但不是Greeter
。有关详细信息,请参阅Go,X 未实现 Y(...方法有指针接收器) 。有关更多详细信息和深入介绍,请阅读:Go 博客:反射定律
编辑:关于修改副本
我的答案的第一部分:每当您传递某些内容时,都会制作一份副本。这还包括将接口值传递给reflect.ValueOf()
: ,它也会收到一个副本,因此您无法避免这一点。完成这项工作的唯一方法是传递一个指针;因为指针也会被复制,但我们正在修改相同的指向值(而不是指针)。