HandlerFunc(f)如何将函数转换为接口类型?

时间:2018-08-05 11:56:43

标签: go

在检查以下代码时,对于将类型从函数转换为接口存在疑问。


代码

http_hello.go:

package main

import (
    "fmt"
    "log"
    "net/http"
)

// hello http,
func helloHttp() {
    // register handler,
    http.Handle("/", http.HandlerFunc(helloHandler))

    // start server,
    err := http.ListenAndServe(":9090", nil)
    if err != nil {
        log.Fatal("ListenAndServe:", err)
    }

}

// handler function - hello,
func helloHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, you've requested: %s\n", r.URL.Path)
}

func main() {
    helloHttp()
}

上面的代码有效。

(然后,我尝试编写一个小程序来检查这是否是一项常规功能,但无法正常工作,请检查以下代码)

func_to_intf.go:

package main

import (
    "fmt"
)

// an interface,
type Adder interface {
    add(a, b int) int
}

// alias of a function signature,
type AdderFunc func(int, int) int

// a simple add function,
func simpleAdd(a, b int) int {
    return a + b
}

// call Adder interface to perform add,
func doAdd(a, b int, f Adder) int {
    return f.add(a, b)
}

func funcToIntf() {
    fa := AdderFunc(simpleAdd)
    fmt.Printf("%#v, type: %T\n", fa, fa)

    a, b := 1, 2
    sum := doAdd(a, b, fa)
    fmt.Printf("%d + %d = %d\n", a, b, sum)
}

func main() {
    funcToIntf()
}

输出:

  

./ func_to_intf.go:30:14:不能将fa(类型AdderFunc)用作类型Adder   在doAdd的参数中:AdderFunc不实现Adder(缺少add   方法)


问题

  1. http.HandlerFunc(helloHandler)获得类型为http.Handler的值,因为正是http.Handle()所期望的,这是正确的吗?
  2. 如果是,则意味着它将函数转换为接口类型的值,这是怎么发生的?
    • 这是go的内置功能吗?
      我做了测试(就像上面的func_to_intf.go一样),看来没有。
    • 或者,http.HandlerFunc的特殊实现是否可以实现?

@更新-摘要

(尽管答案很好地解决了这些问题,但是在经过反复审查和更多测试之后,还需要其他一些go功能来完全消除最初的疑问,如下所述。)

  • 函数类型。
    函数是值,并且具有类型。
    可以通过功能签名上的type关键字定义功能类型。
    例如type AdderFunc func(int, int) int
  • 函数上的类型转换器T(v)
    只需通过T(v),任何函数都可以转换为具有相同签名的函数类型,将函数类型名称用作T,将实际函数用作v
    然后,在调用新值时,将调用实际函数v
    例如fa := AdderFunc(simpleAdd)
    (这在问问题之前对我来说是模糊的,这是我感到困惑的主要原因之一)

4 个答案:

答案 0 :(得分:2)

这是一个简单的类型转换。

在Go语言中,您可以定义struct之外的自定义类型。在这种情况下,http.HandlerFunc是函数类型func(http.ResponseWriter,*http.Request)。由于您的函数与自定义类型具有相同的基础类型(签名),因此可以将其转换为它。

此外,代码可以定义自定义类型的方法,无论它是什么基础类型,或者它是否为struct。在这种情况下,http包在其上定义了ServeHTTP方法,当然,它只是调用函数本身。

您可以在此处阅读源代码:https://golang.org/src/net/http/server.go?s=58384:58444#L1936

对于示例代码中的加法器,您可以执行以下操作:在AdderFunc上定义一个方法。

func (a AdderFunc) add(x, y int) int {
    return a(x, y)
}

游乐场:https://play.golang.org/p/5mf_afHLQA2

答案 1 :(得分:1)

不是特殊功能或内置功能。相反,http.HandlerFunc是实现http.Handler接口的类型。看看它的实现是多么漂亮https://github.com/golang/go/blob/d3c3aaa61f7598f275f30fabd3749379fe0f2720/src/net/http/server.go#L1956

答案 2 :(得分:1)

docs说:

  

HandlerFunc类型是一个适配器,允许将普通函数用作HTTP处理程序。如果f是具有适当签名的函数,则HandlerFunc(f)是调用f的处理程序。

所以,它不是一个函数,而是一个包装器类型,声明为:

    final VideoView videoView=findViewById(R.id.videoView);
    videoView.setVideoURI(Uri.parse("android.resource://"+getPackageName()+"/"+R.raw.video));
    final MediaController mediaController=new MediaController(this);
    videoView.setMediaController(mediaController);


    final TextView textView = findViewById(R.id.textView);
    textView.setText("0");

    int topContainerId = getResources().getIdentifier("mediacontroller_progress","id", "android");
    final SeekBar seekbar = (SeekBar) mediaController.findViewById(topContainerId);

    seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
        @Override
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
            int currentValue = Integer.parseInt(textView.getText().toString());
            int newValue = progress / 1000;
            if (newValue != currentValue) {
                textView.setText(String.valueOf(newValue));
            }
        }

        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {
        }

        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {

        }
    });

    videoView.start();

但是Go允许(我认为这是它最大的功能之一),只需定义require方法,就可以使任何新声明的类型实现任何可能的接口。因此,类型type HandlerFunc func(ResponseWriter, *Request) 通过定义方法ServeHttp来实现接口HandlerFunc。该实现只调用包装的函数。

答案 3 :(得分:1)

http.HandlerFunc是通过提供方法http.Handler满足接口http.ServeHTTP(ResponseWriter, *Request)的类型。

http.HandlerFunc(helloHandler)是类型转换,用于转换具有相同基础基本类型但方法集不同的类型。

您以working

为例