menu arrow_back 湛蓝安全空间 |狂野湛蓝,暴躁每天 chevron_right All_wiki chevron_right the-way-to-go_ZH_CN chevron_right eBook chevron_right 11.6.md
  • home 首页
  • brightness_4 暗黑模式
  • cloud
    xLIYhHS7e34ez7Ma
    cloud
    湛蓝安全
    code
    Github
    11.6.md
    3.03 KB / 2024-07-16 23:14:27
        # 11.6 使用方法集与接口
    
    在[第 10.6.3 节](10.6.md)及例子 [methodset1.go](examples\chapter_10\methodset1.go) 中我们看到,作用于变量上的方法实际上是不区分变量到底是指针还是值的。当碰到接口类型值时,这会变得有点复杂,原因是接口变量中存储的具体值是不可寻址的,幸运的是,如果使用不当编译器会给出错误。考虑下面的程序:
    
    示例 11.5 [methodset2.go](examples/chapter_11/methodset2.go):
    
    ```go
    package main
    
    import (
    	"fmt"
    )
    
    type List []int
    
    func (l List) Len() int {
    	return len(l)
    }
    
    func (l *List) Append(val int) {
    	*l = append(*l, val)
    }
    
    type Appender interface {
    	Append(int)
    }
    
    func CountInto(a Appender, start, end int) {
    	for i := start; i <= end; i++ {
    		a.Append(i)
    	}
    }
    
    type Lener interface {
    	Len() int
    }
    
    func LongEnough(l Lener) bool {
    	return l.Len()*10 > 42
    }
    
    func main() {
    	// A bare value
    	var lst List
    	// compiler error:
    	// cannot use lst (type List) as type Appender in argument to CountInto:
    	//       List does not implement Appender (Append method has pointer receiver)
    	// CountInto(lst, 1, 10)
    	if LongEnough(lst) { // VALID: Identical receiver type
    		fmt.Printf("- lst is long enough\n")
    	}
    
    	// A pointer value
    	plst := new(List)
    	CountInto(plst, 1, 10) // VALID: Identical receiver type
    	if LongEnough(plst) {
    		// VALID: a *List can be dereferenced for the receiver
    		fmt.Printf("- plst is long enough\n")
    	}
    }
    ```
    
    **讨论**
    
    在 `lst` 上调用 `CountInto` 时会导致一个编译器错误,因为 `CountInto` 需要一个 `Appender`,而它的方法 `Append` 只定义在指针上。 在 `lst` 上调用 `LongEnough` 是可以的,因为 `Len` 定义在值上。
    
    在 `plst` 上调用 `CountInto` 是可以的,因为 `CountInto` 需要一个 `Appender`,并且它的方法 `Append` 定义在指针上。 在 `plst` 上调用 `LongEnough` 也是可以的,因为指针会被自动解引用。
    
    **总结**
    
    在接口上调用方法时,必须有和方法定义时相同的接收者类型或者是可以根据具体类型 `P` 直接辨识的:
    
    - 指针方法可以通过指针调用
    - 值方法可以通过值调用
    - 接收者是值的方法可以通过指针调用,因为指针会首先被解引用
    - 接收者是指针的方法不可以通过值调用,因为存储在接口中的值没有地址
    
    将一个值赋值给一个接口时,编译器会确保所有可能的接口方法都可以在此值上被调用,因此不正确的赋值在编译期就会失败。
    
    **译注**
    
    Go 语言规范定义了接口方法集的调用规则:
    
    - 类型 `*T` 的可调用方法集包含接受者为 `*T` 或 `T` 的所有方法集
    - 类型 `T` 的可调用方法集包含接受者为 `T` 的所有方法
    - 类型 `T` 的可调用方法集**不**包含接受者为 `*T` 的方法
    
    ## 链接
    
    - [目录](directory.md)
    - 上一节:[测试一个值是否实现了某个接口](11.5.md)
    - 下一节:[第一个例子:使用 Sorter 接口排序](11.7.md)
    
    
    links
    file_download