zoukankan      html  css  js  c++  java
  • golang方法和接口

    一.  go方法

    go方法:在函数的func和函数名间增加一个特殊的接收器类型,接收器可以是结构体类型或非结构体类型。接收器可以在方法内部访问。创建一个接收器类型为Type的methodName方法。

    func (t Type) methodName(parameter list) {
    }

    go引入方法的原因:

    1)go不是纯粹的面向对象编程语言,而且Go不支持类。因此,基于类型的方法是一种实现和类相似行为的途径。

    2)相同名字的方法可以定义在不同的类型上,而相同名字的函数不被允许。

    方法调用

    t.methodName(parameter list)

    指针接收器与值接收器

    区别:指针接收器的方法内部的改变对外可见,而值接收器不会改变方法外部的变量。

    对于指针接收器&T Type而言,(&T).methodName与T.methodName等价。

    匿名字段的方法

    属于结构体的匿名字段的方法可以被直接调用,就好像这些方法是属于定义了匿名字段的结构体一样。

    在方法中使用值接收器 与 在函数中使用值参数

    当一个函数有一个值参数,它只能接受一个值参数。

    当一个方法有一个值接收器,它可以接受值接收器和指针接收器。

    package main
    
    import "fmt"
    
    type rectangle struct {
            length int
            width int
    }
    
    func area(r rectangle){
            fmt.Printf("Area Function result: %d
    ", (r.length * r.width))
    }
    
    func (r rectangle)area(){
            fmt.Printf("Area method result: %d
    ", (r.length * r.width))
    }
    
    func main(){
            r := rectangle{
                    length: 10,
                     5,
            }
    
            area(r)
            r.area()
    
            p := &r
    //      area(p) // cannot use p (type *rectangle) as type rectangle in argument to area
            p.area() //通过指针调用接收器
    }

    在方法中使用指针接收器 与 在函数中使用指针参数

    函数使用指针参数只接受指针,而使用指针接收器的方法可以使用值接收器和指针接收器。

    在非结构体上的方法

    为了在一个类型上定义一个方法,方法的接收器类型定义和方法的定义应该在同一个包中。

    对于内建类型,如int,应该在文件中创建一个类型别名,然后创建一个以该类型别名为接收器的方法。

    二.  go接口

    接口是方法(方法签名,method signature)的集合。当一个类型定义了接口中的所有方法,就称它实现了该接口。与OOP类似,接口定义了一个类型应该具有的方法,由该类型决定如何实现这些方法。

    type myInterface interface{
        method1()
        method2()
    }

    接口调用

    //interface definition
    type VowelsFinder interface {  
        FindVowels() []rune
    }
    
    type MyString string
    
    //MyString implements VowelsFinder
    func (ms MyString) FindVowels() []rune {  
        var vowels []rune
        for _, rune := range ms {
            if rune == 'a' || rune == 'e' || rune == 'i' || rune == 'o' || rune == 'u' {
                vowels = append(vowels, rune)
            }
        }
        return vowels
    }
    
        name := MyString("Sam Anderson")
        var v VowelsFinder
        v = name // possible since MyString implements VowelsFinder
        fmt.Printf("Vowels are %c", v.FindVowels())

    如果一个类型包含了接口中声明的所有方法,那么它就隐式地实现了 Go 接口

    接口的内部表示

    可以把接口看作内部的一个元组 (type, value)。 type 是接口底层的具体类型(Concrete Type),而 value 是具体类型的值。

    type Test interface {  
        Tester()
    }
    
    type MyFloat float64
    
    func (m MyFloat) Tester() {  
        fmt.Println(m)
    }
    
    func describe(t Test) {  
        fmt.Printf("Interface type %T value %v
    ", t, t)
    }
    
    func main() {  
        var t Test
        f := MyFloat(89.7)
        t = f
        describe(t)
        t.Tester()
    }

    输出:

    Interface type main.myFloat value 89.7
    89.7

    空接口

    没有包含方法的接口称为空接口。空接口表示为 interface{}。由于空接口没有方法,因此所有类型都实现了空接口。

    当指定参数为空接口时,可以接收任意类型,那如何获取参数的值呢?  通过类型断言。 v, ok := p.(int),判定参数是否为int并获取参数值。

    类型断言

    类型断言用于提取接口的底层值(Underlying Value)。

    在语法 i.(T) 中,接口 i 的具体类型是 T,该语法用于获得接口的底层值。

    v, ok := i.(T)

    如果 i 的具体类型是 T,那么 v 赋值为 i 的底层值,而 ok 赋值为 true。

    如果 i 的具体类型不是 T,那么 ok 赋值为 false,v 赋值为 T 类型的零值,此时程序不会报错

    类型选择(Type Switch)

    类型选择用于将接口的具体类型与很多 case 语句所指定的类型进行比较。它与一般的 switch 语句类似。

    类型断言的语法是 i.(type),获取接口的类型

    还可以将一个类型和接口相比较。如果一个类型实现了接口,那么该类型与其实现的接口就可以互相比较。

    type Describer interface {
            Describe()
    }
    
    type Person struct {
            name string
            age int
    }
    
    
    func (p Person) Describe(){
            fmt.Printf("%s is %d years old
    ", p.name, p.age)
    }
    
    func findType(i interface{}){
            switch v := i.(type){
                    case Describer:
                            v.Describe()
                    default:
                            fmt.Printf("unknown type
    ")
            }
    }
    
    func main(){
            findType("wang")
            p := Person{
                    name: "qing",
                    age: 25,
            }
    
            findType(p)
    }
    unknown type
    qing is 25 years old

    在上面程序中,结构体 Person 实现了 Describer 接口。在第 19 行的 case 语句中,v 与接口类型 Describer 进行了比较。p 实现了 Describer,因此满足了该 case 语句,于是当程序运行到第 32 行的 findType(p) 时,程序调用了 Describe() 方法。

    实现接口:指针接受者与值接受者

    使用值接受者声明的方法,接口既可以用值来调用,也能用指针调用。

    对于使用指针接受者的方法,必须用一个指针或者一个可取得地址的值(&method)来调用。但接口中存储的具体值(Concrete Value)并不能取到地址,对于编译器无法自动获取 a 的地址,于是程序报错。

    type Describer interface {  
        Describe()
    }
    type Person struct {  
        name string
        age  int
    }
    
    func (p Person) Describe() { // 使用值接受者实现  
        fmt.Printf("%s is %d years old
    ", p.name, p.age)
    }
    
    type Address struct {
        state   string
        country string
    }
    
    func (a *Address) Describe() { // 使用指针接受者实现
        fmt.Printf("State %s Country %s", a.state, a.country)
    }
    
    func main() {  
        var d1 Describer
        p1 := Person{"Sam", 25}
        d1 = p1
        d1.Describe()
        p2 := Person{"James", 32}
        d1 = &p2
        d1.Describe()
    
        var d2 Describer
        a := Address{"Washington", "USA"}
    
        /* 如果下面一行取消注释会导致编译错误:
           cannot use a (type Address) as type Describer
           in assignment: Address does not implement
           Describer (Describe method has pointer
           receiver)
        */
        //d2 = a
    
        d2 = &a // 这是合法的
        // 因为在第 22 行,Address 类型的指针实现了 Describer 接口
        d2.Describe()
    }

    接口的嵌套

    type SalaryCalculator interface {  
        DisplaySalary()
    }
    
    type LeaveCalculator interface {  
        CalculateLeavesLeft() int
    }
    
    type EmployeeOperations interface {  
        SalaryCalculator
        LeaveCalculator
    }

    接口的零值

    接口的零值是 nil。对于值为 nil 的接口,其底层值(Underlying Value)和具体类型(Concrete Type)都为 nil。对于值为 nil 的接口,由于没有底层值和具体类型,当我们试图调用它的方法时,程序会产生 panic 异常。

    Go接口最佳实践

    1)倾向于使用小的接口定义,很多接口只包含一个方法。    如Reader,Writer,便于类型实现接口,方法太多,类型实现越麻烦。

    2)较大的接口定义,可以由多个小接口定义组合而成。  即接口的嵌套。

    3)只依赖于必要功能的最小接口。方法或函数的接口参数的范围或方法越小越好,这样便于参数的调用,和方法或函数被其他程序调用。

    如func StoreData(reader Reader) error{},能传递Reader就不传递ReadWriter。

    参考:Go 系列教程 —— 17. 方法     Golang tutorial series  英文原版

  • 相关阅读:
    【LSGDOJ 1408】邮局
    中间件小姐姐直播“带货”——阿里程序员必知的插件
    ChaosBlade 发布对 C++ 应用混沌实验的支持
    来自 Spring Cloud 官方的消息,Spring Cloud Alibaba 即将毕业
    大合集 | 9 场 Dubbo Meetup 回顾视频和PPT下载
    Future Maker | 领跑亚太 进击的阿里云数据库
    更简单易用的数据仓库,阿里云重磅推出分析型数据库3.0版
    AnalyticDB for MySQL 3.0 技术架构解析
    阿里云 EMAS HTTPDNS 联合函数计算重磅推出 SDNS 服务,三大能力获得突破
    新一代互联网传输协议QUIC浅析
  • 原文地址:https://www.cnblogs.com/embedded-linux/p/11084790.html
Copyright © 2011-2022 走看看