time.AfterFunc(10 * time.Second, func() { r.Launch() }) //这里相当于将r.Launch封装为一个函数传入
直接用方法"值"传入AfterFunc
的话可以更为简短:
time.AfterFunc(10 * time.Second, r.Launch)
和方法"值"相关的还有方法表达式。当调用一个方法时,与调用一个普通的函数相比,我们必须要用选择器(p.Distance)语法来指定方法的接收器。当T是一个类型时,方法表达式可能会写作T.f或者(*T).f,会返回一个函数"值",这种函数会将
其第一个参数用作接收器,所以可以用通常(译注:不写选择器)的方式来对其进行调用:
p := Point{1, 2}
q := Point{4, 6}
//这里Point是类型名,其拥有一个方法func (p Point) Distance(),
distance := Point.Distance // method expression
fmt.Println(distance(p, q)) // "5"
fmt.Printf("%T\n", distance) // "func(Point, Point) float64"
scale := (*Point).ScaleBy
scale(&p, 2)
fmt.Println(p) // "{2 4}"
fmt.Printf("%T\n"
, scale) // "func(*Point, float64)"
以上的内容相当于将类型的一个方法转化为一个函数,该函数相较于方法多了第一个参数,该参数表明接收器,如上述将方法func (p Point) Distance()float64
转化为func(Point, Point) float64
接口
在Go语言中还存在着另外一种类型:接口类型。接口类型是一种抽象的类型。它不会暴露出它所代表的对象的内部值的结构和这个对象支持的基础操作的集合;它们只会展示出它们自己的方法。也就是说当你有看到一个接口类型的值时,你不知道它是什么,唯一知道的就是可以通过它的方法来做什么。
nil 接口值既不保存值也不保存具体类型。
也就是说,如果一个类型声明了某个接口给出的所有方法,则认为该类型继承了该接口,而无需显式说明。
常用内建接口
Stringer
是一个可以用字符串描述自己的类型。fmt
包(还有很多包)都通过此接口来打印值
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}
func main() {
a := Person{"Arthur Dent", 42}
z := Person{"Zaphod Beeblebrox", 9001}
fmt.Println(a, z) //Arthur Dent (42 years) Zaphod Beeblebrox (9001 years)
}
error接口
Go 程序使用 error
值来表示错误状态。与 fmt.Stringer
类似,error
类型是一个内建接口:
type error interface {
Error() string
}
(与 fmt.Stringer
类似,fmt
包在打印值时也会满足 error
。)
http.Handler接口
package http
type Handler interface {
ServeHTTP(w ResponseWriter, r *Request)
}
func ListenAndServe(address string, h Handler) error
ListenAndServe函数需要一个例如“localhost:8000”的服务器地址,和一个所有请求都可以分派的Handler接口实例。它会一直运行,直到这个服务因为一个错误而失败(或者启动失败),它的返回值一定是一个非空的错误。
类型断言
类型断言 提供了访问接口值底层具体值的方式。
t := i.(T)
该语句断言接口值 i
保存了具体类型 T
,并将其底层类型为 T
的值赋予变量 t
。若 i
并未保存 T
类型的值,该语句就会触发一个panic。
这里有两种可能。第一种,如果断言的类型T是一个具体类型,然后类型断言检查x的动态类型是否和T相同。如果这个检查成功了,类型断言的结果是x的动态值,当然它的类型是T。换句话说,具体类型的类型断言从它的操作对象中获得具体的值。如果检查失败,接下来这个操作会抛出panic。例如:
var w io.Writer
w = os.Stdout
f := w.(*os.File) // success: f == os.Stdout
c := w.(*bytes.Buffer) // panic: interface holds *os.File, not *bytes.Buffer
上述中,w为os.Stdout
,其类型为*os.File
,所以w.(*os.File)
断言成功,返回w的动态值,即os.Stdout
。因为w的类型与*bytes.Buffer
不符,所以c :=w.(*bytes.Buffer)
运行时返回panic
如果断言类型T是接口类型,则类型断言检查x的动态类型是否满足T.如果此检查成功,在下面代码中,w.(io.ReadWriter)
检查的是w的动态类型(即os.Stdout
的动态类型io.ReadWriter
),与w的接口io.Writer
无关。
var w io.Writer
w = os.Stdout
rw := w.(io.ReadWriter) // success: *os.File has both Read and Write
w.Write([]byte("w write "))
// w.Read([]byte("w read")) //w.Read undefined (type io.Writer has no field or method Read)
rw.Read([]byte("rw read"))
rw.Write([]byte("rw write"))
在上面的第一个类型断言后,w和rw都持有os.Stdout
因此它们每个有一个动态类型*os.File
,但是变量w是一个io.Write
, r类型只对外公开出文件的Write方法,然而rw变量同时公开它的Read和write方法。
如果断言操作的对象是一个nil接口值,那么不论被断言的类型是什么这个类型断言都会失败。
为了 判断 一个接口值是否保存了一个特定的类型,类型断言可返回两个值:其底层值以及一个报告断言是否成功的布尔值。
t, ok := i.(T)
func main() {
var i interface{} = "hello"
s := i.(string) //i保存的值的具体类型为string
fmt.Println(s)
s, ok := i.(string)
f