• GO方法与接口


    Go语言没有沿袭传统面向对象编程中的诸多概念,比如继承、虚函数、构造函数和析构函数、隐藏的this指针等。

    方法

    Go 语言中同时有函数和方法。方法就是一个包含了接受者(receiver)的函数,receiver可以是内置类型或者结构体类型的一个值或者是一个指针。所有给定类型的方法属于该类型的方法集。

    如下面的这个例子,定义了一个新类型Integer,它和int一样,只是为它内置的int类型增加了个新方法Less()

    复制代码
    type Integer int 
    
    func (a Integer) Less(b Integer) bool {
        return a < b 
    }
    
    func main() {
        var a Integer = 1 
    
        if a.Less(2) {
            fmt.Println("less then 2")
        }   
    }
    复制代码

    可以看出,Go语言在自定义类型的对象中没有C++/Java那种隐藏的this指针,而是在定义成员方法时显式声明了其所属的对象。

    method的语法如下:

    func (r ReceiverType) funcName(parameters) (results)

    当调用method时,会将receiver作为函数的第一个参数:

    funcName(r, parameters);

    所以,receiver是值类型还是指针类型要看method的作用。如果要修改对象的值,就需要传递对象的指针。

    指针作为Receiver会对实例对象的内容发生操作,而普通类型作为Receiver仅仅是以副本作为操作对象,并不对原实例对象发生操作。

    复制代码
    func (a *Ingeger) Add(b Integer) {
        *a += b
    }
    
    func main() {
        var a Integer = 1 
        a.Add(3)
        fmt.Println("a =", a)     //  a = 4
    }
    复制代码

    如果Add方法不使用指针,则a返回的结果不变,这是因为Go语言函数的参数也是基于值传递。

    注意:当方法的接受者是指针时,即使用值类型调用那么方法内部也是对指针的操作。

    之前说过,Go语言没有构造函数的概念,通常使用一个全局函数来完成。例如:

    复制代码
    func NewRect(x, y, width, height float64) *Rect {
        return &Rect{x, y, width, height}
    }   
    
    func main() {
        rect1 := NewRect(1,2,10,20)
        fmt.Println(rect1.width)
    }
    复制代码

    匿名组合

    Go语言提供了继承,但是采用了组合的语法,我们将其称为匿名组合,例如:

    复制代码
    type Base struct {
        name string
    }
    
    func (base *Base) Set(myname string) {
        base.name = myname
    }
    
    func (base *Base) Get() string {
        return base.name
    }
    
    type Derived struct {
        Base
        age int 
    }
    
    func (derived *Derived) Get() (nm string, ag int) {
        return derived.name, derived.age
    }
    
    
    func main() {
        b := &Derived{}
    
        b.Set("sina")
        fmt.Println(b.Get())
    }
    复制代码

    例子中,在Base类型定义了get()和set()两个方法,而Derived类型继承了Base类,并改写了Get()方法,在Derived对象调用Set()方法,会加载基类对应的方法;而调用Get()方法时,加载派生类改写的方法。

    组合的类型和被组合的类型包含同名成员时, 会不会有问题呢?可以参考下面的例子:

    复制代码
    type Base struct {
        name string
        age int
    }
    
    func (base *Base) Set(myname string, myage int) {
        base.name = myname
        base.age = myage
    }
    
    type Derived struct {
        Base
        name string
    }
    
    func main() {
        b := &Derived{}
    
        b.Set("sina", 30)
        fmt.Println("b.name =",b.name, "	b.Base.name =", b.Base.name)
        fmt.Println("b.age =",b.age, "	b.Base.age =", b.Base.age)
    }
    复制代码

    值语义和引用语义

    值语义和引用语义的差别在于赋值,比如

    b = a
    b.Modify()

    如果b的修改不会影响a的值,那么此类型属于值类型;如果会影响a的值,那么此类型是引用类型。

    Go语言中的大多数类型都基于值语义,包括:

    • 基本类型,如byte、int、bool、float32、string等;
    • 复合类型,如arry、struct、pointer等;

    C语言中的数组比较特别,通过函数传递一个数组的时候基于引用语义,但是在结构体定义数组变量的时候基于值语义。而在Go语言中,数组和基本类型没有区别,是很纯粹的值类型,例如:

    var a = [3] int{1,2,3}
    var b = a
    b[1]++
    fmt.Println(a, b)   // [1 2 3] [1 3 3]

    从结果看,b=a赋值语句是数组内容的完整复制,要想表达引用,需要用指针:

    var a = [3] int{1,2,3}
    var b = &a    // 引用语义
    b[1]++
    fmt.Println(a, b)   // [1 3 3] [1 3 3]

    接口

    Interface 是一组抽象方法(未具体实现的方法/仅包含方法名参数返回值的方法)的集合,如果实现了 interface 中的所有方法,即该类/对象就实现了该接口。

    Interface 的声明格式:

    type interfaceName interface {  
        //方法列表  
    }  

    Interface 可以被任意对象实现,一个类型/对象也可以实现多个 interface;
    interface的变量可以持有任意实现该interface类型的对象。

     如下面的例子:

    复制代码
    package main
    
        import "fmt"
    
        type Human struct {
            name string
            age int
            phone string
        }
    
        type Student struct {
            Human //匿名字段
            school string
            loan float32
        }
    
        type Employee struct {
            Human //匿名字段
            company string
            money float32
        }
    
        //Human实现SayHi方法
        func (h Human) SayHi() {
            fmt.Printf("Hi, I am %s you can call me on %s
    ", h.name, h.phone)
        }
    
        //Human实现Sing方法
        func (h Human) Sing(lyrics string) {
            fmt.Println("La la la la...", lyrics)
        }
    
        //Employee重载Human的SayHi方法
        func (e Employee) SayHi() {
            fmt.Printf("Hi, I am %s, I work at %s. Call me on %s
    ", e.name,
                e.company, e.phone)
            }
    
        // Interface Men被Human,Student和Employee实现
        // 因为这三个类型都实现了这两个方法
        type Men interface {
            SayHi()
            Sing(lyrics string)
        }
    
        func main() {
            mike := Student{Human{"Mike", 25, "222-222-XXX"}, "MIT", 0.00}
            paul := Student{Human{"Paul", 26, "111-222-XXX"}, "Harvard", 100}
            sam := Employee{Human{"Sam", 36, "444-222-XXX"}, "Golang Inc.", 1000}
            tom := Employee{Human{"Tom", 37, "222-444-XXX"}, "Things Ltd.", 5000}
    
            //定义Men类型的变量i
            var i Men
    
            //i能存储Student
            i = mike    
            fmt.Println("This is Mike, a Student:")
            i.SayHi()
            i.Sing("November rain")
    
            //i也能存储Employee
            i = tom
            fmt.Println("This is tom, an Employee:")
            i.SayHi()
            i.Sing("Born to be wild")
    
            //定义了slice Men
            fmt.Println("Let's use a slice of Men and see what happens")
            x := make([]Men, 3)
            //这三个都是不同类型的元素,但是他们实现了interface同一个接口
            x[0], x[1], x[2] = paul, sam, mike
    
            for _, value := range x{
                value.SayHi()
            }
        }
    复制代码

    空接口

    空interface(interface{})不包含任何的method,正因为如此,所有的类型都实现了空interface。空interface对于描述起不到任何的作用(因为它不包含任何的method),但是空interface在我们需要存储任意类型的数值的时候相当有用,因为它可以存储任意类型的数值。它有点类似于C语言的void*类型。

    复制代码
    // 定义a为空接口
        var a interface{}
        var i int = 5
        s := "Hello world"
        // a可以存储任意类型的数值
        a = i
        a = s
    复制代码

    interface的变量里面可以存储任意类型的数值(该类型实现了interface),那么我们怎么反向知道这个interface变量里面实际保存了的是哪个类型的对象呢?目前常用的有两种方法:switch测试、Comma-ok断言。

    switch测试如下:

    复制代码
    type Element interface{}
    type List [] Element
    
    type Person struct {
        name string
        age int 
    }
    
    //打印
    func (p Person) String() string {
        return "(name: " + p.name + " - age: "+strconv.Itoa(p.age)+ " years)"
    }
    
    func main() {
        list := make(List, 3)
        list[0] = 1 //an int 
        list[1] = "Hello" //a string
        list[2] = Person{"Dennis", 70} 
    
        for index, element := range list{
            switch value := element.(type) {
                case int:
                    fmt.Printf("list[%d] is an int and its value is %d
    ", index, value)
                case string:
                    fmt.Printf("list[%d] is a string and its value is %s
    ", index, value)
                case Person:
                    fmt.Printf("list[%d] is a Person and its value is %s
    ", index, value)
                default:
                    fmt.Println("list[%d] is of a different type", index)
            }   
        }   
    }
    复制代码

    如果使用Comma-ok断言的话:

    复制代码
    func main() {
        list := make(List, 3)
        list[0] = 1 // an int
        list[1] = "Hello" // a string
        list[2] = Person{"Dennis", 70}
    
        for index, element := range list {
            if value, ok := element.(int); ok {
                fmt.Printf("list[%d] is an int and its value is %d
    ", index, value)
            } else if value, ok := element.(string); ok {
                fmt.Printf("list[%d] is a string and its value is %s
    ", index, value)
            } else if value, ok := element.(Person); ok {
                fmt.Printf("list[%d] is a Person and its value is %s
    ", index, value)
            } else {
                fmt.Printf("list[%d] is of a different type
    ", index)
            }
        }
    }
    复制代码

    嵌入接口

    正如struct类型可以包含一个匿名字段,interface也可以嵌套另外一个接口。

    如果一个interface1作为interface2的一个嵌入字段,那么interface2隐式的包含了interface1里面的method。

  • 相关阅读:
    学习总结:CSS(二)块级与行级元素特性、盒模型、层模型、BUG与BFC、浮动模型
    学习总结:CSS(一)定义方式、选择器、选择器权重
    html基础知识总结
    js学习总结:DOM节点二(dom基本操作)
    Javascript的作用域和闭包(一)
    js学习总结:DOM节点一(选择器,节点类型)
    jQuery源码解析对象实例化与jQuery原型及整体构建模型分析(一)
    正则表达式基于JavaScript的入门详解
    JavaScript深度克隆(递归)
    电子警察【思想】
  • 原文地址:https://www.cnblogs.com/shanghongyun/p/10665892.html
Copyright © 2020-2023  润新知