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.     基本类型,如byte、int、bool、float32、float64和string等;
  2.     复合类型,如数组(array)、结构体(struct)和指针(pointer)等。
值语义和引用语义                                                                                               
  1. a := 10
  2. b := a
  3. b = b + 1
  4. fmt.Println(b)
  5. fmt.Println(a)
  6. 输出:11,10
  7. c := 10
  8. d := &c
  9. *d += 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类型的零值为false,int类型的零值为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 (r *Rect) area() float64 {
        1. return r.Width * r.Height
    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) (n int, err error)
      4.     Write(buf []byte) (n int, err error)
      5. }
      6. package two
      7. type IStream interface {
      8. Write(buf []byte) (n int, err error)
      9. Read(buf []byte) (n 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.ReadWriter和two.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 IStudent interface {
    2. Go2School()
    3. }
    4. type IPerson interface {
    5. Speak(word string)
    6. Eat(food string)
    7. }
    8. type Animal interface {
    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{},最典型的例子是标

    准库fmt中PrintXXX系列的函数,例如:

    1. func Printf(fmt string, args ...interface{})
    2. func Println(args ...interface{})

    总体来说,interface{}类似于COM中的IUnknown,我们刚开始对其一无所知,但可以通

    过接口查询和类型查询逐步了解它。

    来自为知笔记(Wiz)

    时间: 2024-10-05 21:48:54

    Go面向对象(三)的相关文章

    面向对象三要素

    面向对象三要素 Posted on 2009-12-11 09:06 我不是高手 阅读(3595) 评论(1) 编辑 收藏 面向对象三要素是:封装 继承 多态 封装 封装就是事物抽象为类,把对外接口暴露,将实现和内部数据隐藏. 继承 面向对象编程 (OOP) 语言的一个主要功能就是“继承”.继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展. 通过继承创建的新类称为“子类”或“派生类”. 被继承的类称为“基类”.“父类”或“超类”. 继承的过程,

    面向对象三要素:封装,继承,多态

    面向对象三要素是:封装 继承 多态 封装 封装就是事物抽象为类,把对外接口暴露,将实现和内部数据隐藏. 继承 面向对象编程 (OOP) 语言的一个主要功能就是"继承".继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展. 通过继承创建的新类称为"子类"或"派生类". 被继承的类称为"基类"."父类"或"超类". 继承的过程,就是从一般到特

    PHP面向对象(三)

    一.继承概念 继承性也是面向对象程序设计中的重要特性之一.它是指建立一个新的派生类,从一个先前定义的类中继承数据和函数,而且可以重新定义新的数据类型和函数,从而建立累的层次或等级关系. 格式: [修饰符] class 子类名 extends 父类名 { ... } 假如B类继承A类,那么 从内存上来讲:B继承了A中所有的属性和方法:但是父类中的private部分不能直接访问 从使用上来讲:B继承了A中所有非私有的属性和方法 其中A叫父类(基类). B叫子类(派生类) PHP只支持单继承,不允许多

    JavaScript 面向对象(三) —— 高级篇

    JavaScript 面向对象(一) —— 基础篇 JavaScript 面向对象(二) —— 案例篇 一.json方式的面向对象 首先要知道,js中出现的东西都能够放到json中.关于json数据格式这里推荐一篇博客:JSON 数据格式 先看下json创建的简单对象:相比基础篇中的构造函数.原型等的创建方式,json方式简单方便:但是缺点很明显,如果想创建多个对象,那么会产生大量重复代码,不可取. JSON方式适用于只创建一个对象的情况,代码简介又优雅. 1 <!DOCTYPE html>

    面向对象 三个基本特征 五项基本原则

    三个基本元素: 1. 封装: 封装是把过程和数据包围起来,对数据的访问只能通过已定义的界面.面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治.封装的对象,这些对象通过一个受保护的接口访问其他对象. 2. 继承: 继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法.对象的一个新类可以从现有的类中派生,这个过程称为类继 承.新类继承了原始类的特性,新类称为原始类的派生类(子类),而原始类称为新类的基类(父类).派生类可以从它的基类那里继承方法和实例变

    Python面向对象(三)

    一.绑定方法与非绑定方法 一.绑定方法:绑定给谁就应该由谁来调用,谁来调用就会将谁当作第一个参数传入 1.绑定给对象的方法:类中定义的函数默认就是绑定给对象的 2.绑定给类的方法:为类中定义的函数加上一个装饰器@classmethod 二.非绑定方法:既不与类绑定,又不与对象绑定,意味着对象和类都可以来调用,无论谁来调用都是一个普通的函数,普通函数没有自动传值的效果 案例如下: class Foo: def f1(self): print(self) @classmethod def f2(cl

    面向对象 三 (三大特性)

    封装,继承,多态 一:  了解什么是封装: 就象一个盒子,你不需要知道里面有什么东西,只知道它有那些用处就行,能够为你提供相对应的方法. 封装的意义: 封装的意义在于保护或者防止代码(数据)被我们无意中破坏. 保护成员属性,不让类以外的程序直接访问和修改: 隐藏方法细节 关于对象封装的原则: 内聚:内聚是指一个模块内部各个部分之间的关联程度 耦合:耦合指各个模块之前的关联程度 封装原则:隐藏对象的属性和实现细节,仅对外公开访问方法,并且控制访问级别 在面向对象方法中,用类来实现上面的要求.用类实

    Javascript面向对象三:非构造函数的继承

    一.什么是"非构造函数"的继承? 比如,现在有一个对象,叫做"中国人". var Chinese = { nation:'中国' }; 还有一个对象,叫做"医生". var Doctor ={ career:'医生' } 请问怎样才能让"医生"去继承"中国人",也就是说,我怎样才能生成一个"中国医生"的对象? 这里要注意,这两个对象都是普通对象,不是构造函数,无法使用构造函数方法实现&q

    Python 面向对象 三

    isinstance :检查是否object是类cls的对象,后面可以跟创建自己的类,也可以是基类. 可用于判断excel里面的数值是否是int类型 例如:isinstance(对象,类) issubclass:检查某个类是否是某个类的子类. 例如:issubclass(类,类) 例: class A: pass class B(A): pass b = B() print (isinstance(b,A)) -->True,判断b是由A创建的, print (issubclass(B,A))

    重修课程day22(面向对象三之继承和派生)

    面向对象的三大特性:封装,继承和多态 一 继承 1 什么是继承:1.1 什么是什么的关系.一个类是另一个类的子类. 1.2 继承是一种创新类的方式.新建的类可以继承一个或多个父类.父类又称为基类或超类,新建的类又称为派生类或子类 继承一个父类叫做单继承:继承多个父类叫做多继承. 2 继承的好处:减少代码的冗余,增强了代码的重用性. 二 继承的用法 super:调用父类的功能和方法 格式一:在子类的内部调用父类的属性和方法 class 父类名: def  属性(self): 功能 class 子类