首页 > 其他 > 详细

go基础5-方法

时间:2020-08-18 20:15:57      阅读:66      评论:0      收藏:0      [点我收藏+]

方法

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的内部字段是包内可见,无论你定义在函数还是方法里.
封装的优点:

  • 降低外部调用方使用难度
  • 隐藏实现细节
  • 阻止外部调用方对对象内部的值任意地修改

go基础5-方法

原文:https://www.cnblogs.com/chengmuyu/p/13525227.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!