zoukankan      html  css  js  c++  java
  • go基础5-方法

    方法

    go中同时有函数和方法.方法是包含了接收者( receiver)的函数,接收者可以是命名类型或结构体类型的值或指针.所有给定类型的方法属于该类型的方法集.go中的方法是一种有绑定行为的特殊函数,绑定行为用于标识函数所属.

    方法声明

    方法:在函数名称前增加变量的声明方式.这个附加的参数会将该函数附加到这种类型上,即为变量类型定义一个独占的方法.方法上的变量必须属于一种类型,不能使用原始类型.

    type Point struct{ X, Y float64 }
    
    // 函数
    func Distance(p, q Point) float64 {
        return math.Hypot(q.X-p.X, q.Y-p.Y)
    }
    
    // 方法 表示Distance是Point的一个方法
    func (p Point) Distance(q Point) float64 {
        return math.Hypot(q.X-p.X, q.Y-p.Y)
    }
    
    // 声明类型
    type I int
    
    func (i I) Sum(q I) int {
        return int(i) + int(q)
    }
    

    上面变量p称为方法的接收器;接收器命名建议使用变量的首字母,简短一致;
    方法调用方式:使用所属方式

    p := Point{1, 2}
    q := Point{4, 6}
    fmt.Println(Distance(p, q)) // "5", 函数调用
    fmt.Println(p.Distance(q))  // "5", 方法调用
    

    这种所属方式使用形式叫做选择器.它会根据变量后的名称选择相应的方法或字段来使用.
    对于给定的类型,其内部方法的命名必须唯一,不同类型可以使用相同方法名;

    基于指针对象的方法

    函数调用使用指针可以避免参数拷贝,数据修改更加直接;&取地址;*取表达式值

    func (p *Point) ScaleBy(factor float64) {
        p.X *= factor
        p.Y *= factor
    }
    

    一般会约定如果Point类有一个指针作为接收器的方法,那么所有Point的方法都必须有一个指针接收器,及时哪些不需要指针接收器的函数.????

    方法声明中,不允许类型名本身就是指针的接收器.

    type P *int
    func (P) f() { /* ... */ } // compile error: invalid receiver type
    

    调用指针类型方法(*Point).ScaleBy,只需要提供一个Point类型指针即可:

    // 方法一
    r := &Point{1, 2}
    r.ScaleBy(2)
    fmt.Println(*r)
    // 方法二
    p := Point{1, 2}
    pptr := &p
    pptr.ScaleBy(2)
    fmt.Println(p)
    // 方法三
    q := Point{1, 2}
    (&q).ScaleBy(2)
    fmt.Println(q)
    //方法四
    p.ScaleBy(2)
    

    方法四中go会隐式用&p去调用方法,但它只适用于变量.
    不管接收器是指针类型还是非指针类型,都是可以通过指针/非指针类型调用,编译器会帮你做类型转换;
    指针非指针类型的选择要看对象本身是否比较大,是否需要原值修改;
    声明为非指针变量时,调用会产生一次拷贝;
    指针类型接收器会指向同一内存地址,操作同一个数据;

    嵌入结构体来扩展类型

    type ColoredPoint struct {
        Point
        Color color.RGBA
    }
    

    通过组合方式来扩展数据类型,调用时也跟定义在自身上一致.如:cp.X=cp.Point.X.但是嵌入结构体只是类型组合不是继承关系.
    可以定义匿名字段,用于直接使用类型的方法.

    方法值和方法表达式

    方法调用可以拆分为两个步骤:方法绑定和调用

    p := Point{1, 2}
    q := Point{4, 6}
    p.Distance(q)
    <===>
    distanceFromP := p.Distance        // method value
    distanceFromP(q)      // "5"
    

    p.Distance会返回将方法和特定接收器变量绑定的函数,后续使用传入函数参数即可,不用在指定接收器.

    方法表达式中类型T调用可以省略接收器变量:

    distance := Point.Distance
    fmt.Println(distance(p, q))
    

    类似于静态变量.

    bit数组

    bit数组适用于集合间的并集,交集操作.通常使用无符号数或称为"字"的slice来表示.
    每个元素的每一位都表示集合里的一个值.每个元素:指位数的分组数.如1byte=8bit,13/8代表有两个byte,就是有两个元素;元素里每一位都会进行值存储;当集合的第i位被设置时,我们才说这个集合包含元素i.

    // 值添加
    func (s *IntSet) Add(x int) {
        word, bit := x/64, uint(x%64)
        for word >= len(s.words) {
            s.words = append(s.words, 0)
        }
        s.words[word] |= 1 << bit
    }
    // 取值
    func (s *IntSet) String() string {
        var buf bytes.Buffer
        buf.WriteByte('{')
        for i, word := range s.words {
            if word == 0 {
                continue
            }
            for j := 0; j < 64; j++ {
                if word&(1<<uint(j)) != 0 {
                    if buf.Len() > len("{") {
                        buf.WriteByte(' ')
                    }
                    fmt.Fprintf(&buf, "%d", 64*i+j)
                }
            }
        }
        buf.WriteByte('}')
        return buf.String()
    }
    

    封装

    封装:调用方不可见对象中的变量或方法.go中只有一种可见性控制手段:大小写标识符;这种限制同样适用于struct或类型的方法;

    type IntSet struct {
        words []uint64
    }
    
    type IntSet []uint64
    

    上面两种写法的底层实现一致,但是字段的外部可见性不同.但是struct的内部字段是包内可见,无论你定义在函数还是方法里.
    封装的优点:

    • 降低外部调用方使用难度
    • 隐藏实现细节
    • 阻止外部调用方对对象内部的值任意地修改
  • 相关阅读:
    java 标准异常
    java 重新抛出异常
    java 异常链
    java 轨迹栈
    mysql死锁-非主键索引更新引起的死锁
    数据库事务
    JMS学习笔记(一)
    log4j中将SocketAppender将日志内容发送到远程服务器
    Kubernetes之kubectl常用命令
    java代理与动态代理的学习
  • 原文地址:https://www.cnblogs.com/chengmuyu/p/13525227.html
Copyright © 2011-2022 走看看