英文:
Pointer to interface with saving type
问题
我问题的最简短解释是这段代码:
var i interface{} // 我不能改变它。实际上这是一个函数,
i = Item{10} // 接收 interface{},其中包含对象(而不是对象的指针!)
fmt.Printf("%T %v\n", i, i)
// fmt.Println(i.(NextValuer).NextVal()) // 无法编译通过
i = &i
fmt.Printf("%T %v\n", i, i) // 这里 i 是指向 interface{} 的指针(而不是指向 Item 的指针)
// fmt.Println(i.(NextValuer).NextVal()) // 引发 panic
// fmt.Println(i.(*NextValuer).NextVal()) // 无法编译通过
但是,如果我尝试将 Item 的指针设置给 i,代码就可以工作:
i = &Item{10}
fmt.Printf("%T %v\n", i, i)
fmt.Println(i.(NextValuer).NextVal())
但是我的函数接收的是对象,而不是对象的指针。我可以获取它的类型(第一个 `fmt.Printf`)。但是当我尝试创建指向它的指针时,我得到的是指向 `interface{}` 的指针,而不是指向我的对象(`Item`)的指针。
我能否创建指向该对象的指针来调用 `NextVal`?或者还有其他方法可以实现吗?
<details>
<summary>英文:</summary>
The shortest way to explain my problem is [that code](http://play.golang.org/p/cTZYcz_1BX):
var i interface{} // I can't change it. In fact this is a function,
i = Item{10} // that receives interface{}, that contain object (not pointer to object!)
fmt.Printf("%T %v\n", i, i)
// fmt.Println(i.(NextValuer).NextVal()) // won't compile
i = &i
fmt.Printf("%T %v\n", i, i) // there i is pointer to interface{} (not to Item)
// fmt.Println(i.(NextValuer).NextVal()) // panics
// fmt.Println(i.(*NextValuer).NextVal()) // won't compile
But if I try to set pointer to Item to i, code works:
i = &Item{10}
fmt.Printf("%T %v\n", i, i)
fmt.Println(i.(NextValuer).NextVal())
But my function receives object, not pointer to it. And I can get type of it (first `fmt.Printf`). But when I try to make pointer to it, I receive pointer to `interface{}`, not to my Object (`Item`).
Can I make pointer to this object to call `NextVal`? Or may be other ways to do it
</details>
# 答案1
**得分**: 1
永远不要使用指向接口的指针。如果你需要使用指针调用一个带有指针接收器的方法,那么你必须将指针放入`interface{}`中。
如果你已经在`interface{}`中有一个值,并且你想调用一个带有指针接收器的方法,你需要创建一个可寻址的该值的副本。
你可能想要实现的是:
item := i.(Item)
i = &item
这将创建原始`Item`的可寻址副本,然后将该副本的指针放入`i`中。请注意,这永远不会改变原始`Item`的值。
如果你不知道`interface{}`中可能包含的类型,你可以使用"reflect"创建该值的副本:
func nextVal(i interface{}) {
// 获取i中的值
v := reflect.ValueOf(i)
// 创建一个与i相同类型的新值的指针
n := reflect.New(v.Type())
// 使用i的值设置新值
n.Elem().Set(v)
// 将新指针作为接口获取,并调用NextVal
fmt.Println("NextVal:", n.Interface().(NextValuer).NextVal())
// 这也可以分配给另一个interface{}
i = n.Interface()
nv, ok := i.(NextValuer)
fmt.Printf("i is a NextValuer: %t\nNextVal: %d\n", ok, nv.NextVal())
}
http://play.golang.org/p/gbO9QGz2Tq
<details>
<summary>英文:</summary>
Never use a pointer to an interface. If you need a pointer to call a method with a pointer receiver, a pointer is what you must put into the `interface{}`.
If you have already have value in an `interface{}` where you want to call a method with a pointer receiver, you need to make an addressable copy of that value.
What you're trying to accomplish with `i = &i` is probably:
item := i.(Item)
i = &item
This creates an addressable copy of the original `Item`, and then puts a pointer to that copy into `i`. Note that this can never change the value of the original `Item`.
If you don't know the type that can be in the `interface{}`, you can make a copy of the value with "reflect":
func nextVal(i interface{}) {
// get the value in i
v := reflect.ValueOf(i)
// create a pointer to a new value of the same type as i
n := reflect.New(v.Type())
// set the new value with the value of i
n.Elem().Set(v)
// Get the new pointer as an interface, and call NextVal
fmt.Println("NextVal:", n.Interface().(NextValuer).NextVal())
// this could also be assigned another interface{}
i = n.Interface()
nv, ok := i.(NextValuer)
fmt.Printf("i is a NextValuer: %t\nNextVal: %d\n", ok, nv.NextVal())
}
http://play.golang.org/p/gbO9QGz2Tq
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论