• Go 面向对象概念


    前言: 本文是学习<<go语言程序设计>> -- 清华大学出版社(王鹏 编著) 的2014年1月第一版 做的一些笔记 , 如有侵权, 请告知笔者, 将在24小时内删除, 转载请注明出处!

    1. 对象

      - 任意简单的内置数据类型

      - 任意复杂的结构体

      - 表示具体的事物 / 抽象的规则 / 计划 / 事件 等.

    2. 对象的状态

      - 用数值来描述, 如长方体的长和宽等.

    3. 对象的操作

      - 用于改变对象的状态, 操作就是对象的行为.

      - GO语言中称为Method(方法), Method 就是在 函数(function) 前面增加了一个接收者(Receiver)对象. 将操作和对象关联起来了.

        - 定义:   func (recv receiver_type) methodName(args)(rets){}    Receiver 可以是: 内置类型/自定义类型/结构体/指针类型

        - 多个Method可以同名, 只要接收者不同, 就是不同的Method.(类似于重载吧)

        - Method可以访问接收者的字段, 而不需要将字段作为参数传入Method, 就像在struct中访问字段一样.

        - 普通类型作为Receiver,是值传递;  指针类型作为Receiver, 将传递引用.

    package main
    
    import (
        "fmt"
        "math"
    )
    
    type rect struct {
        width  int
        height int
    }
    type circle struct {
        radius float32
    }
    
    func (recv rect) area() int {
        return recv.width * recv.height
    }
    func (recv circle) area() float32 {
        return recv.radius * recv.radius * math.Pi
    }
    func main() {
        r1 := rect{4, 3}
        r2 := rect{30, 15}
        fmt.Println(r1.area(), r2.area())
        c := circle{5}
        fmt.Println(c.area())
    }
    View Code

       - Receiver

        - 匿名Receiver, 省略了receiver的名字(类型没有省,所以可以判断), 此时不能定义同名的方法(应该是会造成无法将操作和对象绑定). 

       - Method 继承

        - go中可以通过匿名字段实现字段继承;  如果匿名字段实现了一个Method(或者说是这个Method的Receiver), 那么包含这个匿名字段的struct对象也能调用该Method.

        - 可以重写继承的方法, 对象会像处理匿名字段一样, 优先处理外部同名Method.

    4. 接口(Interface)

      - 一组Method的组合, 可以通过Interface来定义对象的一组行为, 如果某个对象实现了某个接口的所有方法, 那么它就实现了该接口. 无须显式在该类型上添加接口说明.

      - 习惯以 "er" 结尾, 如: Printer, Reader, Writer.

      - 一个Interface中包含的Method不宜过多, 一般 0 - 3个

      - Interface可以被任意的对象实现, 一个对象也可以实现多个 Interface.

      - 接口组合

        - 类似类型的匿名组合(如结构体, Method) ,接口也可以组合: 将一个/多个接口匿名嵌入另外一个接口中 , 就组合了接口.

    type SpeakListener interface{
        Speaker // Speaker为一个匿名接口
        Learner // Learner 为一个匿名接口
    }

      - 空接口 : 任何数据类型都默认实现了空接口.   interface {} 

        - 可以用来定义任意类型的参数和返回值. (有点儿像是void 和 void* 的用处吧) : 1.  func f1(a interface{}){}    2.  func f2(a ... interface{}){} 

      - 接口的执行机制和赋值

        - 接口是引用类型.(如何理解呢? )

        - 接口是可以被实例化的类型, 当定义一个接口类型变量时, 系统会为其分配内存, 并将赋给它的对象赋值到这个内存区域.

        - 接口对象(由 Itab指针和data指针组成)

    struct Iface{  // C语言中的表示
        Itab * tab;
        void * data;
    };

          - Itab : 依据data类型创建, 存储了接口动态调用的元数据信息, 其中包括data类型所有符合接口签名的方法地址列表. 使用接口对象调用方法时就从Itab中查找对应的方法, 并将 *data 作为Receiver参数传递给该方法.

            - 编译器在构建 Itab时, 区分T 和 * T 方法集, 病从中获取接口实现方法的地址指针, 接口调用不会做Receiver自动转换, 目标方法必须在接口实现的方法集中. 接口方法集规则:  

              - T 仅仅拥有T类型的方法集, 而 *T拥有(T+*T)方法集

              - 基于T实现方法, 表示同事实现了interface(T) 和interface(*T) 接口

              - 基于(*T)实现方法, 就只能是对 interface(*T)实现接口

        - 接口的定义与赋值

          - 定义了一个interface的变量, 那么变量里面可以存储实现了这个interface的任意类型的对象. 

          - 多个对象同时实现了这个接口的话, 那么可以用这个接口作为类型, 定义一个slice, 那么就可以写出比较少的代码. ix = make([]Speaker,3)   

        - 结构体的匿名字段方法和接口转换

          - 当接口类型是struct时, 这些struct可能有匿名字段, 而为这些匿名字段定义的方法也会被接口所继承. 

          - 接口之间可以相互包含.  == > 超级(范围大) + 子集(范围小)

            - 匿名字段方法

            - 接口支持struct匿名字段实现的方法, 因为外层结构"继承"了匿名字段的方法集. 规则如下(好奇怪的规则, 没看懂, 先记下来?), S和T都是struct类型的.

              - 如果S中嵌入匿名类型T, 则S的方法集包含T的方法集.

              - 如果S中嵌入匿名类型* T, 则S的方法集包含*T的方法集(T+*T)

              - 如果S中嵌入匿名类型 T 或 (我怀疑应该是"和") * T, 则 *S的方法集合包含 *T的方法集 (T+*T)  

              - 贴段没看明白的代码(可以正常运行)

    package main
    import (
        "fmt"
    )
    type People struct{
        Name string
    }
    type Teacher struct{
        People
        Department string
    }
    func (p People)SayHi(){
        fmt.Println(p.Name)
    }
    type Speaker interface{
        SayHi()
    }
    func main() {
        p := People{"Roger"}
        t := Teacher{People{"roger"},"CS"}
        var is Speaker
        is = &p // 为 *People定义了SayHi(),自然实现该接口 (Why?)
        p.SayHi()
        is.SayHi()
        is = &t
        is.SayHi()
    }
    View Code

           - 接口转换

            - 拥有超集的接口还可以被转换成拥有子集的接口, 这样子集(结构体)就可以很容易地访问超集(结构体)对象中的成员变量或者数据.

            - 贴段代码

    package main
    
    import (
        "fmt"
    )
    type People struct {
        Name string
        Age int
    }
    type Student struct{
        People
        School string
    }
    type PeopleInfo interface{
        GetPeopleInfo()
    }
    type StudentInfo interface{
        GetStudentInfo()
        GetPeopleInfo()
    }
    func (p People) GetPeopleInfo() {
        fmt.Println(p)
    }
    func (s Student) GetStudentInfo(){
        fmt.Println(s)
    }
    func main() {
        var is StudentInfo = Student{People{"Roger",18},"Sichuan Uni"}
        is.GetStudentInfo()
        is.GetPeopleInfo()
        //接口StudentInfo拥有PeopleInfo的全部方法签名, PeopleInfo属于StudentInfo的一个子集(可以从方法数量上看),
        //所以可以直接将接口类型变量is 赋值给ip. 类似于
        // Student是People的子类, 继承就是任何子类 is a 父类.
        var ip PeopleInfo = is 
        ip.GetPeopleInfo()
    }
    View Code

       - 接口类型推断

        - 通过接口赋值, 可以知道接口类型变量中可以存储任意类型的数值. 要想反向知道接口类型变量里保存的具体对象类型, 就可以用接口类型推断.

        - 两种方法

          - Comma-ok断言

    package main
    
    import (
        "fmt"
    )
    
    type People struct {
        Name string
        Age  int
    }
    type Tester interface { // 定义空接口用于存储任意类型
    }
    
    func main() {
        p := People{"roger", 20}
        it := make([]Tester, 4)
        it[0], it[1], it[2], it[3] = 1, "Hello", p, true
        for index, element := range it {
            // 没看明白这个 ok 是干啥的?
            if value, ok := element.(int); ok {
                fmt.Printf("t[%d] is an int. value = %d
    ", index, value)
            } else if value, ok := element.(string); ok {
                fmt.Printf("t[%d] is an string. value = %v
    ", index, value)
            } else if value, ok := element.(People); ok {
                fmt.Printf("t[%d] is an People type. value = %v
    ", index, value)
            } else {
                fmt.Printf("t[%d] is an unknown type.
    ", index)
            }
        }
    }
    View Code

          - switch测试

            - 上面的代码换成switch case就是了.

        

    5. 反射

      - 标准库: reflect, 提供TypeOf() , ValueOf() 方法 从 interface{}接口对象中获取实际目标对象的类型和值信息.

      - 返回的Type和Value有大量的方法, 常用的有kind() 和 Float()等.

    package main
    
    import (
        "fmt"
        "reflect"
    )
    
    type Student struct {
        Id    int
        Name  string
        Sex   bool
        Grade float32
    }
    
    func (s Student) SayHi() {
        fmt.Println("Hi")
    }
    func (s Student) MyName() {
        fmt.Println("My name is : ", s.Name)
    }
    
    //反射处理函数
    func StructInfo(o interface{}) {
        t := reflect.TypeOf(o)
        if k := t.Kind(); k != reflect.Struct {
            fmt.Printf("This value is not a struct, it's %v.", k)
            return
        }
        fmt.Println("Struct name is", t.Name())
        fmt.Println("Fields of struct is : ")
        v := reflect.ValueOf(o)
        for i := 0; i < t.NumField(); i++ {
            field := t.Field(i)
            value := v.Field(i).Interface()
            fmt.Printf(" %6s : %v = %v
    ", field.Name, field.Type, value)
        }
        fmt.Println("Method of the struct is:")
        // 获取方法的Name和Type信息
        for i := 0; i < t.NumMethod(); i++ {
            method := t.Method(i)
            fmt.Printf("%6s : %v 
    ", method.Name, method.Type)
        }
    }
    
    func main() {
        s := Student{10001, "Roger", false, 99.5}
        StructInfo(s)
    }
    View Code

       - 上面代码中注意:

        - 定义结构体中的字段名和方法名时要大写, 否则反射编译器可能会报错.

        - 反射会将匿名字段当作一个独立字段来处理, 如果要获取嵌入字段的Type 和Value信息, 必须使用索引路径.  通过Value类型的 FieldByIndex() 获取嵌入字段的索引路径.  func (v Value) FieldByIndex(index []int) Value 

        - 如果对一个非Struct类型执行FieldByIndex()将会产生panic错误.

  • 相关阅读:
    pandas 生成excel
    身份证校验规则
    css 模态框
    python3 打开MySQL时:RuntimeError: 'cryptography' package is required for sha256_password or caching_sha2_password auth methods 报错
    selenium元素定位
    LR的基本知识
    python3的编码报错解决办法
    MySQL的简单条件判断语句
    Java判断一个字符串中包含另一字符串
    使用线程池获取执行结果,CountDownLatch+ThreadPool,FutureTask+ThreadPool 并比较
  • 原文地址:https://www.cnblogs.com/roger9567/p/4846394.html
Copyright © 2020-2023  润新知