–
第11章 11.1 接口提供了一种方式来 说明 对象的行为:如果谁能搞定这件事,它就可以用在这儿。这是 多态 的 Go 版本 接口定义了一组方法(方法集),但是这些方法不包含(实现)代码:它们没有被实现(它们是抽象的)。接口里也不能包含变量。 通过如下格式定义接口: type Namer interface { Method1(param_list) return_type Method2(param_list) return_type ... } 上面的 Namer 是一个 接口类型。 (按照约定,只包含一个方法的)接口的名字由方法名加 [e]r 后缀组成,例如 Printer、Reader、Writer、Logger、Converter 等等。还有一些不常用的方式 (当后缀 er 不合适时),比如 Recoverable,此时接口名以 able 结尾,或者以 I 开头(像 .NET 或 Java 中那样)。 一个接口类型的变量或一个 接口值,它本质上是一个指针,可以指向方法 类型不需要显式声明它实现了某个接口:接口被隐式地实现。多个类型可以实现同一个接口。 实现某个接口的类型(除了实现接口方法外)可以有其他的方法。 一个类型可以实现多个接口。 接口类型可以包含一个实例的引用, 该实例的类型实现了此接口(接口是动态类型)。 即使接口在类型之后才定义,二者处于不同的包中,被单独编译:只要类型实现了接口中的方法,它就实现了此接口。 所有这些特性使得接口具有很大的灵活性。 type Shaper interface { Area() float32 } type Square struct { side float32 } func (sq *Square) Area() float32 { return sq.side * sq.side } func main() { sq1 := new(Square) sq1.side = 5 var areaIntf Shaper areaIntf = sq1 // shorter,without separate declaration: // areaIntf := Shaper(sq1) // or even: // areaIntf := sq1 fmt.Printf("The square has area: %f\n", areaIntf.Area()) } 下面这个示例更有代表性 type stockPosition struct { ticker string sharePrice float32 count float32 } /* method to determine the value of a stock position */ func (s stockPosition) getValue() float32 { return s.sharePrice * s.count } type car struct { make string model string price float32 } /* method to determine the value of a car */ func (c car) getValue() float32 { return c.price } /* contract that defines different things that have value */ type valuable interface { getValue() float32 } func showValue(asset valuable) { fmt.Printf("Value of the asset is %f\n", asset.getValue()) } func main() { var o valuable = stockPosition{"GOOG", 577.20, 4} showValue(o) o = car{"BMW", "M3", 66500} showValue(o) } 11.2 一个接口可以包含一个或多个其他的接口,这相当于直接将这些内嵌接口的方法列举在外层接口中一样。 type ReadWrite interface { Read(b Buffer) bool Write(b Buffer) bool } type Lock interface { Lock() Unlock() } type File interface { ReadWrite Lock Close() } 11.3 检测和转换接口变量的类型 varI 必须是一个接口变量 应该总是使用上面的方式来进行类型断言 检测接口类型变量的类型是哪个,在执行过程中动态类型可能会有所不同,但是它总是可以分配给接口变量本身的类型。通常我们可以使用 类型断言 来测试 在某个时刻 varI 是否包含类型 T 的值 if v, ok := varI.(T); ok { // checked type assertion Process(v) return } 如果转换合法,v 是 varI 转换到类型 T 的值,ok 会是 true;否则 v 是类型 T 的零值,ok 是 false,也没有运行时错误发生。 下面是示例 type Shaper interface { Area() float32 } type Square struct { side float32 } var areaIntf Shaper if t, ok := areaIntf.(*Square); ok { fmt.Printf("The type of areaIntf is: %T\n", t) } 注意:如果忽略 areaIntf.(*Square) 中的 * 号,会导致编译错误 11.4 当判断比较多,可以使用下面的 switch t := areaIntf.(type) { case *Square: fmt.Printf("Type Square %T with value %v\n", t, t) case *Circle: fmt.Printf("Type Circle %T with value %v\n", t, t) case nil: fmt.Printf("nil value: nothing to check?\n") default: fmt.Printf("Unexpected type %T\n", t) } 11.5 测试某个值是否实现的某个接口 假定 v 是一个值,然后我们想测试它是否实现了 Stringer 接口,可以这样做: type Stringer interface { String() string } if sv, ok := v.(Stringer); ok { fmt.Printf("v implements String(): %s\n", sv.String()) // note: sv, not v } 接口是一种契约,实现类型必须满足它,它描述了类型的行为,规定类型可以做什么。 接口彻底将类型能做什么,以及如何做分离开来,使得相同接口的变量在不同的时刻表现出不同的行为,这就是多态的本质。 使用接口使代码更具有普适性。 行为:吃 不同类型:鸡,狗,猫 怎么吃:鸡:啄,狗:咬,猫:嚼 三个对象都可以实现吃这种接口, 但是具体怎么吃,接口不管。不同类型对应的吃会有不同的方法 先创建不同的对象,接着创建接口变量,将不同对象赋值给接口变量,接口的方法就可以展现不同的行为 11.6 作用于变量上的方法实际上是不区分变量到底是指针还是值的 在接口上调用方法时,必须有和方法定义时相同的接收者类型或者是可以从具体类型 P 直接可以辨识的: 指针方法可以通过指针调用 值方法可以通过值调用 接收者是值的方法可以通过指针调用,因为指针会首先被解引用(函数接收值,传入的是指针,可以) 接收者是指针的方法不可以通过值调用,因为存储在接口中的值没有地址(函数接收指针,传入的是值,不可以) 将一个值赋值给一个接口时,编译器会确保所有可能的接口方法都可以在此值上被调用,因此不正确的赋值在编译期就会失败。 11.7 这里的例子还不懂,为啥没有创建Sort接口变量,并将数组传递给Sort接口变量 还有:data []*day 这个是什么类型变量,day类型的指针切片 package main import ( "../sort" "fmt" ) func ints() { data := []int{74, 59, 238, -784, 9845, 959, 905, 0, 0, 42, 7586, -5467984, 7586} a := sort.IntArray(data) //conversion to type IntArray sort.Sort(a) if !sort.IsSorted(a) { panic("fails") } fmt.Printf("The sorted array is: %v\n", a) } func strings() { data := []string{"monday", "friday", "tuesday", "wednesday", "sunday", "thursday", "", "saturday"} a := sort.StringArray(data) sort.Sort(a) if !sort.IsSorted(a) { panic("fail") } fmt.Printf("The sorted array is: %v\n", a) } type day struct { num int shortName string longName string } type dayArray struct { data []*day } func (p *dayArray) Len() int { return len(p.data) } func (p *dayArray) Less(i, j int) bool { return p.data[i].num < p.data[j].num } func (p *dayArray) Swap(i, j int) { p.data[i], p.data[j] = p.data[j], p.data[i] } func days() { Sunday := day{0, "SUN", "Sunday"} Monday := day{1, "MON", "Monday"} Tuesday := day{2, "TUE", "Tuesday"} Wednesday := day{3, "WED", "Wednesday"} Thursday := day{4, "THU", "Thursday"} Friday := day{5, "FRI", "Friday"} Saturday := day{6, "SAT", "Saturday"} data := []*day{&Tuesday, &Thursday, &Wednesday, &Sunday, &Monday, &Friday, &Saturday} a := dayArray{data} sort.Sort(&a) if !sort.IsSorted(&a) { panic("fail") } for _, d := range data { fmt.Printf("%s ", d.longName) } fmt.Printf("\n") } func main() { ints() strings() days() } ================================================= package sort type Sorter interface { Len() int Less(i, j int) bool Swap(i, j int) } func Sort(data Sorter) { for pass := 1; pass < data.Len(); pass++ { for i := 0; i < data.Len()-pass; i++ { if data.Less(i+1, i) { data.Swap(i, i+1) } } } } func IsSorted(data Sorter) bool { n := data.Len() for i := n - 1; i > 0; i-- { if data.Less(i, i-1) { return false } } return true } // Convenience types for common cases type IntArray []int func (p IntArray) Len() int { return len(p) } func (p IntArray) Less(i, j int) bool { return p[i] < p[j] } func (p IntArray) Swap(i, j int) { p[i], p[j] = p[j], p[i] } type StringArray []string func (p StringArray) Len() int { return len(p) } func (p StringArray) Less(i, j int) bool { return p[i] < p[j] } func (p StringArray) Swap(i, j int) { p[i], p[j] = p[j], p[i] } // Convenience wrappers for common cases func SortInts(a []int) { Sort(IntArray(a)) } func SortStrings(a []string) { Sort(StringArray(a)) } func IntsAreSorted(a []int) bool { return IsSorted(IntArray(a)) } func StringsAreSorted(a []string) bool { return IsSorted(StringArray(a)) } 11.7中的例子需要好好理解,很重要 https://github.com/unknwon/the-way-to-go_ZH_CN/blob/master/eBook/11.7.md 11.9 空接口或者最小接口 不包含任何方法,它对实现不做任何要求: type Any interface {} 11.10-11.14 未看
–
评论前必须登录!
注册