zoukankan      html  css  js  c++  java
  • Go面向对象(三)

    go语言中的大多数类型都是值予以,并且都可以包含对应的操作方法,在需要的时候你可以给任意类型增加新方法。二在实现某个接口时,无需从该接口集成,只需要实现该接口要求的所有方法即可。任何类型都可以被any类型引用。any类型是空接口 interface{}

    Go语言中,你可以给任意类型(包括内置类型,但不包括指针类型)添加相应的方法  .如下
      1. package main

      2. import (

      3. "fmt"

      4. )

      5. func main() {

      6.     person.Go2School()

      7. }

      8. func (ps Person) Go2School() {

      9.     fmt.Println("go to school")

      10. }

      11.  

    Go语言中的大多数类型都基于值语义,包括:
    1.     基本类型,如byteintboolfloat32float64string等;
    2.     复合类型,如数组(array)、结构体(struct)和指针(pointer)等。
     
    值语义和引用语义                                                                                               
    1. := 10

    2. := a

    3. = b + 1

    4. fmt.Println(b)

    5. fmt.Println(a)

    6. 输出:11,10

    7. := 10

    8. := &c

    9. *+= 1

    10. fmt.Println(c)

    11. fmt.Println(*d)

    12. 输出:11,11

    Go语言中有4个类型比较特别,看起来像引用类型
            数组切片:指向数组(array)的一个区间。
            map:极其常见的数据结构,提供键值查询能力。
            channel:执行体(goroutine)间的通信设施。
            接口(interface):对一组满足某个契约的类型的抽象
    
    
    
    
    结构体                                                                                                        
      1. 定义:

      2. type Rect struct {

      3.     x, y float64

      4.     width, height float64

      5. }

      6. 初始化

      7. rect1 := new(Rect)

      8. rect2 := &Rect{}

      9. rect3 := &Rect{0, 0, 100, 200}

      10. rect4 := &Rect{width: 100, height: 200}

      构造函数                                                                                                                                                                                                          
    Go语言中,未进行显式初始化的变量都会被初始化为该类型的零值,例如bool类型的零值为falseint类型的零值为0,string类型的零值为空字符串。
    Go语言中没有构造函数的概念,对象的创建通常交由一个全局的创建函数来完成,以NewXXX来命名,表示“构造函数”:
    1. package main

    2. //person类

    3. type Person struct {

    4.     Name string

    5.     Age int

    6.     Sex string

    7. }

    8. //person构造函数

    9. func NewPerson(name string, sex string, age int) *Person {

    10.     return &Person{Name: name, Sex: sex, Age: age}

    11. }

    12. //person类ResetName1方法 (传值)

    13. func (ps Person) ResetName1(name string) {

    14.     ps.Name = name

    15. }

    16. //person类ResetName1方法 (传址)

    17. func (ps *Person) ResetName2(name string) {

    18.     ps.Name = name

    19. }

    20. //student类

    21. type Student struct {

    22.     Class string

    23.     Grade string

    24.     *Person

    25. }

    26. //student构造函数

    27. func NewStudent(name string, sex string, age int, class string, grade string) *Student {

    28.     return &Student{Person: NewPerson(name, sex, age), Class: class, Grade: grade}

    29. }

     
     
    匿名组合:类的继承是使用了匿名组合的方式                                                                         
      1. package main

      2. type Person struct {

      3.     Name string

      4.     Age int

      5.     Sex string

      6. }

      7. //构造函数

      8. func NewPerson(name string, sex string, age int) *Person {

      9.     return &Person{Name: name, Sex: sex, Age: age}

      10. }

      11. func (ps Person) ResetName1(name string) {

      12.     ps.Name = name

      13. }

      14. func (ps *Person) ResetName2(name string) {

      15.     ps.Name = name

      16. }

      17. //继承自Person

      18. type Student struct {

      19.     Class string

      20.     Grade string

      21.     Person //或者*Person

      22. }

      23. 这样Student就继承自了 Person

       
    可见性                                                                                                         
    1. 要使某个符号对其他包(package)可见(即可以访问),需要将该符号定义为以大写字母开头

      1. type Rect struct {

      2.     X, Y float64

      3.     Width, Height float64

      4. }

    2. 这样,Rect类型的成员变量就全部被导出了,可以被所有其他引用了Rect所在包的代码访问到。

      1. func (*Rect) area() float64 {

        1. return r.Width * r.Height

      2. } 

    3. 这样,Rect的area()方法只能在该类型所在的包内使用。 

    4. Go语言中符号的可访问性是包一级的而不是类型一级的

     
    接口                                                                                                                                                                                                             
    1. 入侵接口
            c#和Java中的接口时入侵是接口
    2. 非入侵接口
         go的接口是非入侵式接口
    Go语言中,一个类只需要实现了接口要求的所有函数,我们就说这个类实现了该接口
    type File struct {
    // ...
    }
    func (f *File) Read(buf []byte) (n int, err error)
    func (f *File) Write(buf []byte) (n int, err error)
    func (f *File) Seek(off int64, whence int) (pos int64, err error)
    func (f *File) Close() error
    这里我们定义了一个File类,并实现有Read()Write()Seek()Close()等方法。设想我们有如下接口:
    type IFile interface {
    Read(buf []byte) (n int, err error)
    Write(buf []byte) (n int, err error)
    Seek(off int64, whence int) (pos int64, err error)
    Close() error
    }
    type IReader interface {
        Read(buf []byte) (n int, err error)
    }
    type IWriter interface {
        Write(buf []byte) (n int, err error)
    }
    type ICloser interface {
        Close() error
    }
    尽管File类并没有从这些接口继承,甚至可以不知道这些接口的存在,但是File类实现了这些接口,可以进行赋值:
    var file1 IFile = new(File)
    var file2 IReader = new(File)
    var file3 IWriter = new(File)
    var file4 ICloser = new(File)  
    Go语言的非侵入式接口,看似只是做了很小的文法调整,实则影响深远。
    其一,Go语言的标准库,再也不需要绘制类库的继承树图。你一定见过不少C++、Java、C#类库的继承树图。在Go中,类的继承树并无意义,你只需要知道这个类实现了哪些方法,每个方法是啥含义就足够了。
    其二,实现类的时候,只需要关心自己应该提供哪些方法,不用再纠结接口需要拆得多细才合理。接口由使用方按需定义,而不用事前规划。
    其三,不用为了实现一个接口而导入一个包,因为多引用一个外部的包,就意味着更多的耦合。接口由使用方按自身需求来定义,使用方无需关心是否有其他模块定义过类似的接口  
     
     
    接口赋值                                                                                                                                                                                                             
    接口赋值在Go语言中分为如下两种情况:
    1.  将对象实例赋值给接口;
    2.  将接口实例赋值给接口;
    1. //对象赋值给接口

      1. var interfaces IStudent = NewStudent("Jessica", "male", 18, "class1", "grade1")

      2. interfaces.Go2School()

    Go语言中,只要两个接口拥有相同的方法列表(次序不同不要紧),那么它们就是等同的,可以相互赋值  
      1. package one

      2. type ReadWriter interface {

      3.     Read(buf []byte) (int, err error)

      4.     Write(buf []byte) (int, err error)

      5. }

      6. package two

      7. type IStream interface {

      8. Write(buf []byte) (int, err error)

      9. Read(buf []byte) (int, err error)

      10. }

       
    这里我们定义了两个接口,一个叫one.ReadWriter,一个叫two.Istream,两者都定义Read()Write()方法,只是定义次序相反。one.ReadWriter先定义了Read()再定义了Write(),而two.IStream反之。
    Go语言中,这两个接口实际上并无区别,因为:
    1.           任何实现了one.ReadWriter接口的类,均实现了two.IStream
    2. 任何one.ReadWriter接口对象可赋值给two.IStream,反之亦然;
    3. 在任何地方使用one.ReadWriter接口与使用two.IStream并无差异。
    以下这些代码可编译通过:
      1. var file1 two.IStream = new(File)

      2. var file2 one.ReadWriter = file1

      3. var file3 two.IStream = file2

    接口赋值并不要求两个接口必须等价。如果接口A的方法列表是接口B的方法列表的子集,那么接口B可以赋值给接口A。例如,假设我们有Writer接口:
    type Writer interface {
              Write(buf []byte) (n int, err error)
    }
    就可以将上面的one.ReadWritertwo.IStream接口的实例赋值给Writer接口:
    var file1 two.IStream = new(File)
    var file4 Writer = file1
     
    接口查询                                                                                                                                                                                                            
              switch进行接口查询
      1. // OOPTest project main.go

      2. package main

      3. type IStudent interface {

      4.     Go2School()

      5. }

      6. type IPerson interface {

      7.     Speak(word string)

      8.     Eat(food string)

      9. }

      10. func main() {

      11. //std := NewStudent("Jessica", "male", 18, "class1", "grade1")

      12. //psn := NewPerson("James", "female", 20)

      13. //语句switch中的value必须是接口类型,变量str的类型为转换后的类型。/

      14. var IStd interface{} = NewStudent("Jessica", "male", 18, "class1", "grade1")

      15. switch per := IStd.(type) {

      16.     case IStudent:

      17.         per.Go2School()

      18.     case IPerson:

      19.         per.Eat("pig")

      20. }

      21. }

       
    类型断言
      1. // OOPTest project main.go

      2. package main

      3. type IStudent interface {

      4.     Go2School()

      5. }

      6. type IPerson interface {

      7.     Speak(word string)

      8.     Eat(food string)

      9. }

      10. func main() {

      11. //std := NewStudent("Jessica", "male", 18, "class1", "grade1")

      12. //psn := NewPerson("James", "female", 20)

      13. //语句switch中的value必须是接口类型,变量str的类型为转换后的类型。

      14. var IStd interface{} = NewStudent("Jessica", "male", 18, "class1", "grade1")

      15. //switch per := IStd.(type) {

      16. //case IStudent:

      17. // per.Go2School()

      18. //case IPerson:

      19. // per.Eat("pig")

      20. //}

      21. //上面的转换有一个问题,如果该值不包含一个字符串,则程序会产生一个运行时错误。为了避免这个问题,可以使用“comma, ok”的习惯用法来安全地测试值是否为一个字符串:

      22. if types, ok := IStd.(IStudent); ok {

      23.     types.Go2School()

      24. } else if types, ok := IStd.(IPerson); ok {

      25.     types.Eat("pig")

      26. }

      27. }

    接口组合:接口的继承                                                                                                                                                                                               

    1. type IStudentinterface{
    2. Go2School()
    3. }
    4. type IPersoninterface{
    5. Speak(word string)
    6. Eat(food string)
    7. }
    8. type Animalinterface{
    9. IPerson
    10. IStudent
    11. }
    Any类型                                                                                                                                                                                                                      
    由于Go语言中任何对象实例都满足空接口interface{},所以interface{}看起来像是可
    以指向任何对象的Any类型,如下:
    1. var v1 interface{}=1// 将int类型赋值给interface{}
    2. var v2 interface{}="abc"// 将string类型赋值给interface{}
    3. var v3 interface{}=&v2 // 将*interface{}类型赋值给interface{}
    4. var v4 interface{}=struct{ X int}{1}
    5. var v5 interface{}=&struct{ X int}{1}
    当函数可以接受任意的对象实例时,我们会将其声明为interface{},最典型的例子是标
    准库fmtPrintXXX系列的函数,例如:
    1. func Printf(fmt string, args ...interface{})
    2. func Println(args ...interface{})
    总体来说,interface{}类似于COM中的IUnknown,我们刚开始对其一无所知,但可以通
    过接口查询和类型查询逐步了解它。



  • 相关阅读:
    第十一周学习总结
    个人冲刺——(六)
    第二阶段冲刺—第二天
    软件工程第十四周总结
    第二阶段冲刺—第一天
    大道至简阅读笔记02
    输入法用户体验评价
    软件工程第十三周总结
    人机交互-水王
    大道至简阅读笔记01
  • 原文地址:https://www.cnblogs.com/anbylau2130/p/4240844.html
Copyright © 2011-2022 走看看