Day4
方法
- GO没有类,但可以为结构体类型定义方法。
- 方法是一种带特殊接受者参数的函数(方法是函数!),方法接受者位于
func关键字和函数名()之间
type Vertex struct {
X,Y float64
}
func (v Vertex) Add() float64 {//Add方法拥有一个名为 v ,类型为 Vertex 的接受者
return v.X + v.Y
}
func main() {
v := Vertex{3, 4}
fmt.Println(v.Add())
}
运行结果为:
7
type MyFloat float64 //非结构体类型也可以声明方法
func (f MyFloat) Abs() float64 {
if(f < 0){
return float64(-f)
}
return float64(f)
}
func main(){
f := MyFloat(-4.04)
fmt.Println(f.Abs())
}
运行结果:
4.04
- 使用值接受者,调用方法修改修原始值的副本(修改形参);而调用指针接受者会修改原始值(修改时参),来感受下被指针支配的恐惧吧!
type Vertex struct {
X, Y float64
}
func (v Vertex) Scale(f float64) {//v Vertex 形参
v.X = v.X * f
v.Y = v.Y * f
}
func (v *Vertex) _Scale_(f float64) {//v *Vertex 实参
v.X = v.X * f
v.Y = v.Y * f
}
func main() {
v := Vertex{3, 4}
v.Scale(10)
fmt.Println(v) // 3 ,4
v._Scale_(10)
fmt.Println(v)// 30 40
}
运行结果:
{3 4}
{30 40}
type MyNum int
func (n MyNum) Add() int {
n += 1
return int(n)
}
func (n *MyNum) _Add_() int {
*n += 1
return int(*n)
}
func main() {
n := MyNum(1)
n.Add()
fmt.Println(n) // 1
n._Add_()
fmt.Println(n)// 2
}
运行结果:
1
2
- 带指针参数的函数
func name(p *type) return-type必须接受指针,而以指针为接受者的方法被调用时func (r recipient) name() return-type,接受者可以为值或指针 - 带值参数的函数
func name(p *type) return-type必须接受值,而以值为接受者的方法被调用时func (r recipient) name() return-type,接受者可以为值或指针
type Vertex struct {
X, Y float64
}
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func ScaleFunc(v *Vertex, f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
func main() {
v := Vertex{3, 4}//p type为值
v.Scale(2)
ScaleFunc(&v, 10)//&v
p := &Vertex{4, 3}//p type为指针
p.Scale(2)
ScaleFunc(p, 10)//p
fmt.Println(v, p) //{60 80} &{80 60}
}
运行结果
{60 80} &{80 60}
- 使用指针作为接受者的原因:
- 方法能够修改其接受者指向的值
- 这样可以避免每次调用方法时复制该值(若值的类型为大型结构体时,这样做会更加高效)
接口
- 接口类型 是由一组方法签名定义的集合。接口类型的变量可以保存任何实现了这些方法的值
type Abser interface {
Abs() float64
}
type MyFloat float64
func (f MyFloat) Abs() float64{
if f < 0{
return float64(-f)
}
return float64(f)
}
type Vertex struct {
X,Y float64
}
func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X+v.Y+v.Y)
}
func main() {
var a Abser
f := MyFloat(-math.Sqrt2) //MyFloat 实现了 Abser
v := Vertex{3,4} //*Vertex实现Abser
a = f
a= &v
fmt.Println(a)
}
- 接口也是值。它们可以像其他值一样转递。在内部,接口值可以看做包含值和具体类型的元组:
(value, type)
type I interface{
M()
}
type T struct {
S string
}
func (t *T) M() {
fmt.Println(t.S)
}
func describe(i I){
fmt.Printf("(%V,%T)\n",i,i)
}
func main() {
var i I
i = &T{"Hello"}
describe(i)
i.M()
}
运行结果:
(&{%!V(string=Hello)},*main.T)
Hello
- 即使接口内的具体仍为nil,方法仍然会nil接受者调用
type I interface {
M()
}
type T struct {
S string
}
func (t *T) M(){
if t == nil {
fmt.Println("<nil>")
return
}
fmt.Println(t.S)
}
func describe(i I) {
fmt.Printf("(%v,%T)\n",i,i)
}
func main() {
var i I
var t *T
i = t
describe(i)
i.M()
i = &T{"Hello"}
describe(i)
i.M()
}
运行结果:
(<nil>, *main.T) <nil>
(&{hello}, *main.T)
hello
nil接口调用方法时会产生错误,因为nil接口没有指明该调用哪个具体方法的类型
type I interface {
M()
}
func describe(i I){
fmt.Printf("(%v,%t)\n",i,i)
}
func main() {
var i I
describe(i)
i.M()
}
运行结果:
(<nil>,%!t(<nil>))
panic: runtime error: invalid memory address or nil pointer dereference
紧急:运行时错误:无效的内存地址或空指针被引用
- 空接口
interface{}可以保存任意类型的(因为每个类型至少实现了零个方法),可用来处理未知类型的值
func describe(i interface{}){
fmt.Printf("(%v,%T)\n",i,i)
}
func main(){
var i interface{}
describe(i)
i = 42
describe(i)
i = "Hello"
describe(i)
}
运行结果:
(<nil>,<nil>)
(42,int)
(Hello,string)
- 类型断言:提供了访问接口值底层具体值的方式
t := i.(T)如果接口值保存了具体类型T,并将其底层类型T的值赋予变量t,否则会引起一个恐慌为了判断一个接口值是否保存了一个特定的类型,类型断言可返回两个值:其底层值以及一个报告断言是否成功的布尔值t, ok := i.(T)
func main(){
var i = interface {} = "hello"
s := i.(string)
fmt.Println(s)
s, ok := i.(string)
fmt.Println(s,ok)
f, ok := i.(float64)
fmt.Println(f,ok)
f = i.(float64)//报错 panic
fmt.Println(f)
}
运行结果:
hello
hello true
0 false
panic: interface conversion: interface {} is string, not float64
- 类型选择:按顺序从几个类型断言中选择分支的结构
switch v := i.(type) {
case T:
//v 的类型为 T
case S:
//v 的类型为 S
default:
// 没有匹配
}
func do(i interface{}){
switch v := i.(type){
case int:
fmt.Printf("Twice %v is %v\n",v,v*2)
case string:
fmt.Printf("%q is %v bytes long\n",v,len(v))
default:
fmt.Printf("I don't know about type %T\n",v)
}
}
- 练习:Stringer:
func (ip IPAddr) String() string{
return fmt.Sprintf("%v.%v.%v.%v\n",ip[0],ip[1],ip[2],ip[3])
}
运行结果:
loopback: 127.0.0.1
googleDNS: 8.8.8.8
错误
-用error值来表示错误状态,error类型是一个内建接口
type error interface{
Error() string
}
type MyError struct{
When time.Time
What string
}
func (e *MyError) Error() string{ //重写Error
return fmt.Sprintf("at %v,%s",e.When,e.What)
}
func run() error{//函数,返回一个error
return &MyError{
time.Now(),
"it didn't work",
}
}
func main() {
if err := run();err!=nil{
fmt.Println(err)
}
}
type ErrNegativeSqrt float64
func (e ErrNegativeSqrt) Error() string{
return fmt.Sprint("cannot Sqrt negative number:",float64(e))
}
func Sqrt(x float64) (float64, error) {
if x<0 {
return 0,ErrNegativeSqrt(x)
}
return 0, nil
}
func main() {
fmt.Println(Sqrt(2))
fmt.Println(Sqrt(-2))
}
运行结果
0 <nil>
0 cannot Sqrt negative number:-2
Reader
- 练习:
func (m MyReader) Read(b []byte) (int, error) {
b[0] = "A"
return 1 nil// 返回nil,没有结束符
}
func main() {
reader.Validate(MyReader{})
}
type rot13Reader struct {
r io.Reader
}
func (self rot13Reader) Read (b []byte) (int, error) {
n, err := self.r.Read(b)
for i := 0;i <= n;i++ {
switch {
case b[i] >= 'a' && b[i] <= 'm':
b[i] = b[i] + 13
case b[i] >= 'n' && b[i] <= 'z':
b[i] = b[i] - 13
case b[i] >= 'A' && b[i] <= 'M':
b[i] = b[i] + 13
case b[i] >= 'N' && b[i] <= 'Z':
b[i] = b[i] - 13
case err != nil:
return n, err
}
}
return n, err
}
func main() {
s := strings.NewReader(
"Lbh penpxrq gur pbqr!")
r := rot13Reader{s}
io.Copy(os.Stdout, &r)
}
图像
- image 定义了Image接口
package image
type Image interface {
ColorModel() color.Model
Bounds() Rectangle
At(x, y int) color.Color
}
- 练习
```Go
import (
"image"
"image/color"
"golang.org/x/tour/pic"
)
type Image struct {
W int
H int
}
func (self Image) Bounds() image.Rectangle {
return image.Rect(0, 0, self.W, self.H)
}
func (self Image) ColorModel() color.Model {
return color.RGBAModel
}
func (self Image) At(x, y int) color.Color {
return color.RGBA{uint8(x), uint8(y), 255, 255}
}
func main() {
m := Image{W: 100, H: 100}
pic.ShowImage(m)
}
后面的Reader和Image的练习不会写,看来这个接口还是多熟悉啊!
才疏学浅,请赐教了🙏