Go - 29 Go 结构体之方法

基本介绍
    在某些情况下,我们需要声明(定义)方法,比如Person结构体,除了有一些字段外(年龄,姓名...), Person结构体还有一些行为比如:可以说话,跑步...通过学习,还可以做算术题,这时就要用方法才能完成;
    Golang中的方法是作用在指定的数据类型上的(即,和指定的数据类型绑定),因此自定义类型,都可以有方法,而不仅仅是struct;
    基本使用:
        type Person struct {    
                Name string      
                Age  int  
        } 
        func (p Person) eat() {     
                fmt.Println(p.Name, "可以吃东西~~~") 
        } 
         func main() {     
                // 结构体的方法     
                var p Person      
                p.Name = "mary"     
                p.Age = 30  
                // 调用方法   
                p.eat()    // mary 可以吃东西~~~
        }
        上面的一个说明:
            1.eat方法和Person 类型绑定;
            2.eat方法只能通过Person类型的变量来调用,而不能直接调用,也不能使用其他类型变量来调用。
            3.func (p Person)eat() {} ---> p 表示哪个Person变量调用,这个p就是他的副本,这点和函数传参非常相似。
            4. p这个名字,由程序员指定,不是固定;
 
方法的调用和传参机制
        1.在通过一个变量去调用方法时,其调用机制和函数一样
        2.不一样的地方是,变量调用方法时,该变量本身也会作为一个参数传递到方法(如果变量是值类型,则进行值拷贝,如果变量是引用类型,则进行地址拷贝)
 
方法的声明(定义)
    func (recevier  type)  methodName (参数列表) (返回值列表){
            方法体
            return  返回值
    }
    参数列表,表示方法输入;
    receiver  type : 表示这个方法和type这个类型进行绑定,或者说该方法作用于type类型;
    receiver  type:  type 表示可以是结构体,也可以是其他的自定义类型;
    receiver: 就是type类型的一个变量(实例),比如:Person结构体的一个变量(实例)
    返回值列表,表示返回的值,可以多个
    方法主体,表示为了实现某一功能代码块
    return 语句不是必须的(有返回值列表就必须有return);
 
方法注意事项和细节
    1.结构体类型是值类型,在方法调用中,遵守值类型的传递机制,是值拷贝传递方式;
    2.如果程序员希望在方法中,修改结构体变量的值,可以通过结构体指针的方式来处理
    3.Golang中的方法作用在指定的数据类型上的(即:和指定的数据类型绑定)因此自定义类型,都可以有方法,而不仅仅是struct, 比如int,float32等都可以有方法;
    4.方法的访问范围控制的规则,和函数一样,方法名首字母小写,只能在本包访问,方法首字母大写,可以在本包和其他包访问;
    5.如果一个类型实现了String() 这个方法,那么fmt.Println 默认会调用这个变量的String() 进行输出;
    
    示例:
        type Circle struct {    
                Radius float64 
        }
        // 为了提高效率,通常我们方法和结构体的指针类型绑定
        func (c *Circle) area() float64 {     
        // 因为 c 是指针,因此我们标准的访问其字段方式 是 (*c).Radius     
        // return 3.14 * (*c).Radius * (*c).Radius     
        // (*c).radius 等价  c.radius     
        c.Radius = 10     
        return 3.14 * c.Radius * c.Radius 
        }
 
        // 创建一个Circle 变量    
        var c Circle      
        // res := (&c).area()     
        // 编译器底层做了优化 (&c).area() 等价 c.area()     
        // 因为编译器会自动的给加上  &c     
        c.Radius = 7.0     
        res := c.area()     
        fmt.Println("面积是:", res)  // 面积是: 314     
        fmt.Println("c.Radius==", c.Radius)  // c.Radius== 10
 
    实现String方法:
        // 让student 结构体实现 String方法
        type Student struct {     
                Name string      
                Age  int  
        } 
        func (s *Student) String() string {     
                str := fmt.Sprintf("Name=[%v] Age=[%v]", s.Name, s.Age)     
                return str 
        }
        
        var s Student    
        s.Name = "smith"     
        s.Age = 30     
        // 没实现 String方法时,会打印出 &{smith 30}, 但是实现以后 Name=[smith] Age=[30]             
        fmt.Println(&s)
练习:
    结构体实现乘法表打印与二维数组简单转置方法
    // 结构体 MethodUtils 
    type MethodUtils struct {     
            // 结构体也可以没有字段 
    }
    // 打印乘法表
    func (mu MethodUtils) cfb(n int) {     
            for i := 1; i <= n; i ++ {         
                    for j := 1; j <= i; j ++ {             
                            fmt.Printf("%v * %v = %v ", j, i, i * j)         
                    }         
                    fmt.Println()     
            } 
    } 
     // 转置 
    func (mu MethodUtils) zz(ewz [3][3]int) {     
        // 原始打印     
        for _, v := range ewz {         
                for _, v2 := range v {             
                        fmt.Printf("%v ", v2)         
                }         
                fmt.Println()     
        }     
        // 转置打印     
        for i := 0 ; i < len(ewz); i++ {         
                for j := 0; j < len(ewz[i]); j++ {             
                        fmt.Printf("%v ", ewz[j][i])         
                }         
                fmt.Println()     
        } 
    }
 
    // 实例化    
    var mu MethodUtils     
    // 方法调用     
    mu.cfb(9)     
    var ewz = [3][3]int {{1,2,3},{4,5,6},{7,8,9}}     
    mu.zz(ewz)
        
方法与函数的区别
        1.调用方式不一样;
            函数的调用方式:  函数名(实参列表)
            方法调用方式:     变量.方法名(实参列表)
        2.对于普通函数,接收者为值类型时,不能将指针类型的数据直接传递,反之亦然;
        3.对于方法(如struct的方法),接收者为值类型时,可以直接用指针类型的变量调用方法,反过来同样也可以;
        不管调用形式如何,真正决定是值拷贝还是地址拷贝,看这个方法或函数定义的时候和哪个类型绑定的:
        如果和值类型,比如(p  Person) 则是值拷贝;如果是和指针类型,比如是(p *Person) 则是地址拷贝
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
        
posted @ 2020-11-24 19:08  以赛亚  阅读(149)  评论(0编辑  收藏  举报