自学内容网 自学内容网

go语言的面向对象详解

1.go语言面向对象的概念

GO语言的面向对象与传统的java、C#是不一样的。GO里面没有CLASS类的概念。GO语言里使用了结构体替代了class,使用首字母大写来公开对象与方法。GOlang支持面向对象的特性,但并不是纯粹的面向对象语言。GO去掉了传统OOP语言的继承(extends)、方法重载、构造和析构(destructor)函数、隐藏this指针等关键字。

GO语言仍然有编程的继承、封闭和多态的特性,但GO使用了更优雅的接口方式来关联降低耦合性,使用更灵活,GO语言在面向接口编程是非常重要的特性!

面向对象有三个基本特征,封装、继承、多态。

  • 封装就是隐藏对象的属性和实现细节,仅对外公开接口(这里只是广义概念,不是指Interface,是说大写字母开头的方法),控制在程序中属性的读和修改的访问级别。
  • 继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
  • 多态就是同一个行为具有多个不同表现形式或形态的能力。是指一个类实例(对象)的相同方法在不同情形有不同表现形式。多态机制使具有不同内部结构的对象可以共享相同的外部接口。

封装

封装是OOP的一个核心概念,指的是将数据(属性)和操作这些数据的方法(行为)捆绑在一起,并对外部隐藏内部实现细节。在Go语言中,封装主要通过包(package)和可见性规则来实现:

1.  包(Package):Go语言通过包来组织代码。每个包可以包含多个文件,这些文件共享同一个命名空间。包的名称通常是其导入路径的最后一个元素。

2.  可见性规则:Go语言使用首字母大小写来控制变量、类型、函数等的可见性。如果一个标识符以大写字母开头,那么它是公开的(exported),可以在包外访问;如果以小写字母开头,则仅在包内可见。

package myPackage

// Public function
func PublicFunc() {
    // ...
}

// Private function
func privateFunc() {
    // ...
}

 在上面的例子中,PublicFunc 是公开的,可以在其他包中被访问,而 privateFunc 只能在 myPackage 包内部访问。

go和其他语言不同,struct关键字只能定义属性,但是不支持行为,也就是不能定义方法。golang既然支持面向对象那么肯定是能支持方法的,只不过是写法稍微有些不同。 golang是通过下面的方式进行方法绑定的

func (this *Person) GetName() {  
  fmt.Printf(this.Name)
}

func main(){  
  person := Person{  
     Name: "xiaofan",  
     Age:  18,   } 

     person.GetName()  //执行Person绑定的方法GetName   return}
(this *Person)
  • (this *Person)中的this只是一个变量,你不用this,写成(a *Person) 或者(p *Person)也是可以的

  • this *Person的含义为 this = *Person,也就是说把结构体Person的指针指向了this

  • this *Person中Person必须传指针类型,如果写成(this Person)就相当于把Person结构体拷贝了一份给this,那么this在方法中做任何操作都是和结构体没有任何关系的了。

 

继承

Go语言没有传统意义上的类(class)和继承(inheritance)。但是,Go通过接口(interface)和嵌入(embedding)提供了一种替代方案:

1.  接口(Interface):Go中的接口是一种类型,它定义了一组方法签名。任何实现了这些方法的类型都被认为是实现了该接口。这类似于其他语言中的“鸭子类型”或“结构化类型”。

当然,Go中是没有类的,不过,我们可以通过结构体来模拟这些特征。

type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

// 任何实现了Reader和Writer接口的类型都可以被认为是实现了ReadWriter接口
type ReadWriter interface {
    Reader
    Writer
}

2.  嵌入(Embedding):Go 1.18版本引入了类型嵌入,允许将一个类型嵌入到另一个类型中,从而复用字段和方法。
type Base struct {
    // Base fields
}

func (b *Base) BaseMethod() {
    // Base method
}

type Derived struct {
    *Base // 嵌入Base类型
}

// 通过嵌入,Derived类型隐式地拥有了Base的字段和方法

 下面是具体的实现方法

package main  // 声明当前文件属于哪个包

import (
"fmt"  // 导入"fmt"包,用于输出
)

type Tree struct {  // 定义一个结构体Tree,包含三个字段:Value(整型),Left(指向Tree的指针),Right(指向Tree的指针)
Value int    // 节点的值
Left  *Tree  // 指向左子树的指针
Right *Tree  // 指向右子树的指针
}

func (t Tree) setValue1(v int) {  // 定义一个方法setValue1,接收一个整型参数v,t是Tree类型的值接收者
t.Value = v  // 修改t的Value字段为v,由于t是值接收者,这里的修改不会影响调用该方法时传入的Tree实例
}

func (t *Tree) setValue2(v int) {  // 定义一个方法setValue2,接收一个整型参数v,t是Tree类型的指针接收者
t.Value = v  // 修改t指向的Tree实例的Value字段为v,由于t是指针接收者,这里的修改会直接影响调用该方法时传入的Tree实例
}

func main() {  // 主函数,程序的入口
t1 := Tree{  // 创建一个Tree类型的实例t1
Value: 0,  // 初始化Value字段为0
Left:  nil,  // 初始化Left字段为nil,表示没有左子树
Right: nil,  // 初始化Right字段为nil,表示没有右子树
}
t1.setValue1(1)  // 调用setValue1方法,传入参数1,由于是值接收者,不会修改t1的Value字段
fmt.Println(t1)  // 输出t1的值,由于Value字段没有被修改,输出结果为{0 <nil> <nil>}
t1.setValue2(2)  // 调用setValue2方法,传入参数2,由于是指针接收者,会修改t1的Value字段
fmt.Println(t1)  // 输出t1的值,Value字段被修改为2,输出结果为{2 <nil> <nil>}

t2 := &Tree{  // 创建一个Tree类型的指针t2,指向一个Tree实例
Value: 0,  // 初始化Value字段为0
Left:  nil,  // 初始化Left字段为nil,表示没有左子树
Right: nil,  // 初始化Right字段为nil,表示没有右子树
}
t2.setValue1(1)  // 调用setValue1方法,传入参数1,由于是值接收者,不会修改t2指向的Tree实例的Value字段
fmt.Println(t2)  // 输出t2指向的Tree实例的值,由于Value字段没有被修改,输出结果为&{0 <nil> <nil>}
t2.setValue2(2)  // 调用setValue2方法,传入参数2,由于是指针接收者,会修改t2指向的Tree实例的Value字段
fmt.Println(t2)  // 输出t2指向的Tree实例的值,Value字段被修改为2,输出结果为&{2 <nil> <nil>}
}

运行结果:

{0 <nil> <nil>}
{2 <nil> <nil>}
&{0 <nil> <nil>}
&{2 <nil> <nil>}

 

2.go语言的method继承

package main

import "fmt"

type Human struct {
    name  string
    age   int
    phone string
}

type Student struct {
    Human  // 匿名字段
    school string
}

type Employee struct {
    Human   // 匿名字段
    company string
}

// 在 human 上面定义了一个 method
func (h *Human) SayHi() {
    fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}

func main() {
    mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"}
    sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}

    mark.SayHi()
    sam.SayHi()
}

3.结构体的几种定义形式:

package main

import "fmt"

type Student struct {
Name, StuNum, Address string //批量定义三个String类型
}
type miniStudent struct {
stu        Student           //继承Student 的所有属性
Age        int               //年龄
Scores     [2]float32        //数组保存语文和数学的考试成绩
TopPtr     *int              //int类型的指针,用于排名
SliceIndex []int             //int类型的切片,定义学生各项指数,使用的时候需要make
Other      map[string]string //定义一个map用于保存其它额外信息,使用的时候需要make
}

func main() {
var top = 21 //班级排名
var marley miniStudent
marley.stu.Name = "张三"                                             //从Student继承的字段(属性)
marley.stu.Address = "北京-海淀区-中关村-清华大学-门口保安室"                       //从Student继承并显式声明
marley.stu.StuNum = "202001006"                                    //从Student继承,隐匿声明,由编译器自动补全
marley.Age = 10                                                    //年龄
marley.Scores = [2]float32{89.5, 95}                               //语文、数学的成绩
marley.TopPtr = &top                                               //将排名的内存地址传给TopPtr
marley.SliceIndex = []int{10, 20, 30, 40}                          //切片可以自动make,也可以手动先make再使用
marley.Other = map[string]string{"father": "张大牛", "mather": "李金花"} //map可以自动make,也可以手动先make再使用
fmt.Println(marley)                                                //{{张三 202001006 北京-海淀区-中关村-清华大学-门口保安室} 10 [89.5 95] 0xc00002c008 [10 20 30 40] map[father:张大牛 mather:李金花]}

}

运行结果:

{{张三 202001006 北京-海淀区-中关村-清华大学-门口保安室} 10 [89.5 95] 0xc00008c098 [10 20 30 40] map[father:张大牛 mather:李金花]}

 结构体创建实体的更多方式集合:

 使用工厂模式实现构造方法:

package main

import "fmt"

type student struct {
Name string
Age  int
}

// 把要初始化的参数放入到NewStudent这个函数里面,然后返回一个*student的指针里面
func NewStudent(n string, a int) *student {
return &student{
Name: n,
Age:  a,
}
}

// 给student绑定一个私有方法
func (s student) info() {
fmt.Println(s.Name, "的年龄是", s.Age) //打印出对象的信息
}

// 绑定私有方法,可修改名字和年龄
func (s *student) changeName(n string, a int) {
*s = student{n, a} //这是标准写法,*s可以简写为s
}
func main() {
var stu = NewStudent("张三", 10)
fmt.Println(stu)
fmt.Println(*stu)
fmt.Println("stu的类型是:%Tn", stu)
stu.info()
stu.changeName("李四", 18)
stu.info()
}

运行结果:

&{张三 10}
{张三 10}
stu的类型是:%Tn &{张三 10}
张三 的年龄是 10
李四 的年龄是 18

使用 new

func main() {
    xm := new(Profile)
    // 等价于: var xm *Profile = new(Profile)
    fmt.Println(xm)
    // output: &{ 0 }

    xm.name = "iswbm"   // 或者 (*xm).name = "iswbm"
    xm.age = 18     //  或者 (*xm).age = 18
    xm.gender = "male" // 或者 (*xm).gender = "male"
    fmt.Println(xm)
    //output: &{iswbm 18 male}


原文地址:https://blog.csdn.net/2302_79993788/article/details/143380322

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!