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数组适用于集合间的并集,交集操作.通常使用无符号数或称为"字"的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的内部字段是包内可见,无论你定义在函数还是方法里.
封装的优点:
原文:https://www.cnblogs.com/chengmuyu/p/13525227.html