在Go语言中将函数转换为另一种类型(函数强制转换)

4
我最近学到,在net/http包中有一种使用模式总是让我感到困惑。它是函数类型转换。像这样:
(function a) ->convert to-> (type t)
(type t) ->implentments-> (interface i)

因此,如果有一个以接口 i 为其参数的函数,它将调用函数 a,这是 net/http 实现的方式。
但是当我写自己的代码时,我在这个模式上有很多误解。我的代码像这样:
package main

import (
    "fmt"
)

type eat interface {
    eat()
}
type aaa func()

func (op *aaa) eat() {//pointer receiver not right
    fmt.Println("dog eat feels good")
}

///////////////////////////////////////////////
func dog() {
    fmt.Println("I'm a dog")
}
///////////////////////////////////////////////

func feelsGood(a eat) {
    a.eat()
}

func main() {
    b := aaa(dog)
    feelsGood(b)
}

//error:aaa does not implement eat (eat method has pointer receiver)

类型aaa具有方法eat,与接口规则相符的相同函数名称和参数签名,但为什么会出现错误? 接收器是否很重要?
另一个问题是仅使用函数和类型,不包括接口,代码如下:
package main

import (
    "fmt"
)

type aaa func()

func (op *aaa) eat() {
    op()
}

///////////////////////////////////////////////
func dog() {
    fmt.Println("I'm a dog")
}
///////////////////////////////////////////////

func main() {
    obj:=aaa(dog)
    obj.eat()
}
//error:cannot call non-function op (type *aaa)

首先,无论是否出错,op是否为匿名函数?
其次,在我去掉星号后,它可以正常工作,但是为什么? op是类型aaa的实例,接收者是opop代表函数dog()吗?http包使用f(w,r)的方式相同,但有点难理解。 op是一种类型、实例还是匿名函数?
似乎我的函数转换理解不正确,但我也查看了Google上的许多帖子,没有一个能教我如何正确地思考和使用它。谢谢!
2个回答

9

问题1:

在Go语言中,对于类型T(比如你的情况下的aaa),T*T具有不同的方法集。

因此,类型为T的值只能访问以下方法:

func(t T)Foo() { ... }

虽然类型为*T的值可以访问两种方法:

func(t T)Foo() { ... }
func(t *T)Bar() { ... }

在您的情况下,您有两个选项。要么为 aaa 声明 eat 方法而不是 *aaa

func (op aaa) eat() {
    op()
}

或者您将指向b的指针传递给feelsGood函数:

feelsGood(&b)

问题2:

是的,这个问题与第一个问题相关。但在这种情况下,您可以访问该方法,因为obj.eat()将缩写为(&obj).eat()

您在这里的问题是无法在函数指针(op *aaa)上调用函数。您有两个选择,要么为aaa创建方法,而不是*aaa

func (op aaa) eat() {
    op()
}

或者对该值调用op函数,而不是指针:
func (op *aaa) eat() {
    (*op)()
}

但是,我只声明了T类型,T类型甚至不存在,那么一个不存在的T类型怎么能实现一个接口呢?而且,即使T类型存在,T和T之间有什么区别,我唯一能想到的是*T是指向T的指针,一个指针的方法与一个普通方法,这意味着什么?思考! - Dew
我已经阅读了其他关于何时应该使用T函数和何时应该使用T函数的帖子,它们都告诉我,如果您想修改接收器本身,则应使用T,否则应使用T。但在这种情况下,我该如何运用上述考虑来解释应该使用T还是*T方法? - Dew
@user3505400:当声明T时,*T会被隐式声明为指向T的指针,因此它确实存在。是的,区别主要在于您是否可以修改接收器。那么为什么Go不直接在接口中使用值的指针呢?这是因为(据我所知),当您将一个值传递给函数时,例如a := struct{42}; Foo(a),您应该能够放心地保证a不会被更改。 - ANisus

2
关于您问题的第一部分:请参见http://golang.org/doc/faq#different_method_sets,该文档比我能更好地解释了一切。您甚至可以在stackoverflow和golang-nuts邮件列表中搜索此问题,因为这个问题经常出现。
对于第二个问题,在我看来也是一样的:aaa没有一个eat方法(只有*aaa有)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接