指向与保存类型进行交互的接口指针

huangapple go评论86阅读模式
英文:

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&#39;t change it. In fact this is a function,
	i = Item{10}      // that receives interface{}, that contain object (not pointer to object!)

	fmt.Printf(&quot;%T %v\n&quot;, i, i)
	// fmt.Println(i.(NextValuer).NextVal())  // won&#39;t compile
	i = &amp;i
	fmt.Printf(&quot;%T %v\n&quot;, i, i)               // there i is pointer to interface{} (not to Item)
	// fmt.Println(i.(NextValuer).NextVal())  // panics
	// fmt.Println(i.(*NextValuer).NextVal()) // won&#39;t compile

But if I try to set pointer to Item to i, code works:

	i = &amp;Item{10}
	fmt.Printf(&quot;%T %v\n&quot;, 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&#39;re trying to accomplish with `i = &amp;i` is probably:

    item := i.(Item)
	i = &amp;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&#39;t know the type that can be in the `interface{}`, you can make a copy of the value with &quot;reflect&quot;:

    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(&quot;NextVal:&quot;, n.Interface().(NextValuer).NextVal())
    
    	// this could also be assigned another interface{}
    	i = n.Interface()
    	nv, ok := i.(NextValuer)
    	fmt.Printf(&quot;i is a NextValuer: %t\nNextVal: %d\n&quot;, ok, nv.NextVal())
    }

http://play.golang.org/p/gbO9QGz2Tq


</details>



huangapple
  • 本文由 发表于 2016年1月5日 06:12:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/34600817.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定