【GO语言卵细胞级别教程】11.探索Go语言的面向对象编程之美(含源码仅此一份,先到先得)

【GO语言卵细胞级别教程】11.探索Go语言的面向对象编程之美(含源码仅此一份,先到先得)

目录

  • 【GO语言卵细胞级别教程】11.探索Go语言的面向对象编程之美(含源码仅此一份,先到先得)
  • 1.面向对象的引用
    • 1.1简介
    • 1.2结构体
      • 1.2.1定义方式
      • 1.2.2结构体方法
      • 1.2.3结构体方法绑定指定类型
      • 1.2.4方法访问控制
      • 1.2.5方法如果定义了String函数
    • 1.3GO语言的方法和函数
    • 1.4结构体声明方式
    • 1.5使用包导入结构体
    • 1.6结构体嵌入
      • 1.6.1 嵌入方式1:匿名结构体
      • 1.7.2结构体嵌入:当作一个字段
    • 1.7面向对象-封装
      • 1.7.1给她创建一个类吧
      • 1.7.2类的继承
      • 1.7.3多继承
      • 1.7.4注意事项
  • 1.8实现接口
      • 1.8.1语法定义
      • 1.8.2注意事项
  • 1.9多态

🥰微信公众号:【给点知识】分享小知识,快速成长,欢迎关注呀!(底部点击二维码)
🥰本项目演示代码仓库:https://gitee.com/gdzsfun/csdn-study-go 演示项目仓库
🥰本项目创建方式:【GO语言卵细胞级别教程】05.项目创建和函数讲解
🥰学习宗旨:活到老,学到老。
😍写作宗旨:致力于改变现有文章难读难懂问题。
今日分享诗句

1.面向对象的引用

1.1简介

  1. Golang也支持面向对象编程(OOP)但是和传统的面向对象编程有区别,并不是纯碎的面向对象语言,说Golang支持面向对象编程特征是比较准确的
  2. Golang没有类class Go语言的结构体(struct)和其他编程语言的类class 有同等的地位,你可以理解Golang是基于struct 来实现OOP特性的
  3. Golang面向对象编程非常简介,去掉了传统OOP语言的方法重载、构造函数和重构函数、隐藏的this指针等
  4. Golang仍然有面向对象编程的继承、封装和多态的特性,只是实现的方式和其他的OOP语言不一样,比如继承Golang没有extends关键词,继承是通过匿名字段来实现

1.2结构体

1.2.1定义方式

结构体:是值类型,有变量直接指向内存空间

值类型(Value Types):
值类型包括基本数据类型(如整数、浮点数、布尔值等)和结构体(struct)。当我们声明一个值类型的变量时,实际上在内存中分配了一块空间来存储该变量的值。当将一个值类型的变量赋值给另一个变量或作为函数参数传递时,会进行值的复制。这意味着每个变量都有自己的独立内存空间,修改其中一个变量的值不会影响到其他变量。

引用类型(Reference Types):
引用类型包括切片(slice)、映射(map)、通道(channel)、接口(interface)和函数(function)。当我们声明一个引用类型的变量时,实际上在内存中分配了一个指针,指向存储数据的内存地址。这意味着多个变量可以引用同一个内存地址,它们共享相同的数据。当将一个引用类型的变量赋值给另一个变量或作为函数参数传递时,只会复制指针,而不会复制实际的数据。因此,修改其中一个变量的值会影响到其他引用同一份数据的变量。

  1. 结构体定义使用type
package mystudy
//结构体知识点
import "fmt"
// 定义一个人结构体
type person struct{name stringage intschool string
}
  1. 创建结构体的四种方式
func DemoStruct(){fmt.Println("------结构体------")// 声明结构体变量var p1 personp1.name = "张三"p1.age = 19p1.school = "清华大学"fmt.Println(p1)// 声明方式2var p2 person = person{name:"里斯",age:18,school:"理工大学",}fmt.Println(p2)// 声明方式3var p3 = person{name:"王五",age:21,school:"武汉大学",}fmt.Println(p3)// 声明方式4var p4 = person{"王五",22,"武汉大学"}fmt.Println(p4)
}
//输出结果
------结构体------
{张三 19 清华大学}
{里斯 18 理工大学}
{王五 21 武汉大学}
{王五 22 武汉大学}

1.2.2结构体方法

  1. 结构体方法
    结构体方法(Struct Methods)是一种与结构体类型(Struct Type)关联的函数。在 Go 语言中,结构体方法是在结构体类型上定义的函数,可以通过结构体对象调用。
  2. 语法结构
func (接收者类型) 方法名(参数列表) 返回值 {// 方法的实现
}
// 例如
type Persion struct{name string
}
func(p Persion)test(num int)(bool){}

其中,接收者类型指的是与结构体方法关联的结构体类型。在方法定义中,接收者出现在方法名之前,并用括号括起来。参数列表和返回值与普通函数的定义方式相同。
3. 实战

package mystudy
// 面向对象 结构体之方法
import "fmt"type Persion struct{name stringage int
}func(p Persion) persionInfo(){message := fmt.Sprintf("persion info:name:%v,age:%v",p.name, p.age)fmt.Println(message)
}
// 使用指针赋值 因为结构体函数是个
func (p *Persion) set_name(name string)(bool){(*p).name = name// 底层编译器fmt.Println("名字修改成功:", name)return true
}func DemoObjStruct(){fmt.Println("------结构体之方法------")var p = Persion{name:"张三",age:19,}p.persionInfo()p.set_name("韩信")p.persionInfo()
}

以上方法可以看到,实际没有传入指针时,这个结构体方法中是值传递,也就是其他语言说的形参,形参改变不改变原有的数据,所以为了达到set_name 可以设置p.name的效果,这里使用了p.name='xx’来进行设置

1.2.3结构体方法绑定指定类型

在 Go 语言中,我们可以使用接收者(Receiver)来绑定方法。接收者可以是结构体类型或非结构体类型,可以是指针类型或非指针类型。

接收者出现在函数名之前,并用括号括起来。它可以是任何类型,可以是用户自定义的类型,也可以是内置类型。

在方法定义中,接收者的类型决定了该方法与哪种类型关联。接收者可以是值类型(非指针类型)或指针类型。如果接收者是值类型,则方法操作的是接收者的副本,而如果接收者是指针类型,则方法可以修改接收者本身。

以下是几种常见的接收者类型及其绑定方法的示例:

  1. 值类型接收者:
type Rectangle struct {width  float64height float64
}func (r Rectangle) Area() float64 {return r.width * r.height
}
  1. 指针类型接收者:
type Counter struct {count int
}func (c *Counter) Increment() {c.count++
}
  1. 内置类型的接收者:
type MyInt intfunc (m MyInt) IsPositive() bool {return m > 0
}

在上述示例中,我们分别使用值类型和指针类型接收者定义了结构体类型 RectangleCounter 的方法。另外,还使用内置类型 MyInt 定义了一个方法。

要调用绑定的方法,我们需要创建相应的对象,并通过对象调用方法。

rect := Rectangle{width: 5, height: 3}
area := rect.Area()counter := &Counter{}
counter.Increment()num := MyInt(10)
isPositive := num.IsPositive()

在上述示例中,我们通过创建对象 rectcounternum,然后通过对象调用相应的方法来使用绑定的方法。
4. 使用int绑定方法

package mystudy
// 面向对象 结构体之方法
import "fmt"type Integer intfunc (i Integer)absoluteValue()(Integer){fmt.Println("修改之前:", i)if i < 0 {i = -i}fmt.Println("修改之后:", i)return i
}func DemoObjStruct(){fmt.Println("------方法扩展------")var num Integernum = -100num1 := num.absoluteValue()fmt.Println(num)fmt.Println(num1)
}
// 输出结果
修改之前: -100
修改之后: 100
-100
100

1.2.4方法访问控制

结构体绑定的方法和函数一样首字母大写的话可以在本包的其他包访问,搜测只能在本包访问

1.2.5方法如果定义了String函数

  1. 先看代码
package mystudy
// 面向对象 结构体之方法
import "fmt"type Integer intfunc (i Integer)absoluteValue()(Integer){fmt.Println("修改之前:", i)if i < 0 {i = -i}fmt.Println("修改之后:", i)return i
}func(i *Integer)String() string{fmt.Println("===Str===")fmt.Println(*i)fmt.Println("===End===")return "诸葛"
}func DemoObjStruct(){fmt.Println("------方法扩展------")var num Integernum = -100num1 := num.absoluteValue()fmt.Println(&num)fmt.Println(&num1)var nint intfmt.Println(&nint)
}
// 输出结果
------方法扩展------
修改之前: -100
修改之后: 100
===Str===
-100
===End===
诸葛
===Str===
100
===End===
诸葛

可以看到定义了一个方法String() 这个时候当我们调用fmt.Println(&num)传递一个地址的时候,这个时候会调用我们自定义的打印函数,注意一下几个条件,才可以触发我们自定义的函数String

  1. 定义类型 type mytype xxx
  2. 绑定函数 func (x *xxx)String() string{}
  3. 定义变量 var xx mytype
  4. 调用fmt.Println(&xx)传入参数 然后就会触发String函数

1.3GO语言的方法和函数

在 Go 语言中,方法(Method)和函数(Function)有以下区别:

  1. 定义方式:方法是与特定类型关联的函数,因此它们在类型的定义中声明。函数是独立的代码块,可以在任何地方定义。
  2. 接收者:方法具有一个额外的参数,称为接收者(Receiver),它定义了方法与哪个类型相关联。接收者可以是结构体类型或非结构体类型,可以是值类型或指针类型。函数没有接收者。
  3. 调用方式:方法通过类型的对象或指针进行调用,使用点运算符(.)来访问方法。函数直接通过函数名进行调用,不需要使用点运算符。
  4. 修改能力:如果方法的接收者是指针类型,它可以修改接收者本身。这是因为指针接收者传递的是地址,可以对原始对象进行修改。函数不能直接修改参数,因为传递给函数的是参数的副本。
  5. 绑定方式:方法是与类型绑定的,因此它们可以访问类型的属性和方法。函数是独立的,不能直接访问类型的属性和方法,除非将类型的对象作为参数传递给函数。

演示了方法和函数的区别:

package mainimport "fmt"type Rectangle struct {width  float64height float64
}// 方法:计算矩形的面积
func (r Rectangle) Area() float64 {return r.width * r.height
}// 函数:计算两个数的和
func Add(a, b int) int {return a + b
}func main() {rect := Rectangle{width: 5, height: 3}area := rect.Area()fmt.Println("矩形的面积:", area)sum := Add(2, 3)fmt.Println("两个数的和:", sum)
}

方法在调用上也是可以多种方式,可以使用&num 也可以直接使用num,底层都会自动获取,所以这里比较灵活

func (i Integer)absoluteValue()(Integer){fmt.Println("修改之前:", i)if i < 0 {i = -i}fmt.Println("修改之后:", i)return i
}
// 结构体调用方法
fmt.Println("------结构体调用方法------")
fmt.Println((num).absoluteValue())
fmt.Println((&num).absoluteValue())

在上述示例中,Area 是一个方法,它与结构体类型 Rectangle 关联。通过创建 Rectangle 对象 rect,我们可以使用点运算符调用 Area 方法来计算矩形的面积。

Add 是一个函数,它接受两个整数作为参数,并返回它们的和。函数可以直接通过函数名进行调用,并传递参数。

方法可以访问与其关联的类型的属性和方法,而函数则不具备该能力。

总结:方法是与类型关联的函数,具有接收者并可以修改接收者,而函数是独立的代码块,不具有接收者,并且不能直接访问类型的属性和方法。

1.4结构体声明方式

  1. 直接赋值
// 1.直接赋值方式,但顺序不能变
var a1 Stu  = Stu{"张三", 19}
fmt.Println(a1)
  1. 使用key赋值

// 2.使用key赋值:注意最后一个结尾要加逗号
var a2 Stu = Stu{Name:"谢思肖",Age:21,
}
fmt.Println(a2)
  1. 指针声明的两种方式

// 3.指针声明
var a3 *Stu = &Stu{"龙丹丹", 18}
fmt.Println(a3, *a3)
  1. 指针声明使用key赋值

var a4 *Stu = &Stu{Name:"谢思肖",Age:21,
}
fmt.Println(a4, *a4)
  1. 完整的代码
package mystudy
// 面向对象 结构体之方法
import "fmt"
type Stu struct{Name stringAge int
}func DemoObjStruct(){fmt.Println("------结构体声明方法------")// 1.直接赋值方式,但顺序不能变var a1 Stu  = Stu{"张三", 19} fmt.Println(a1)// 2.使用key赋值:注意最后一个结尾要加逗号var a2 Stu = Stu{Name:"谢思肖",Age:21,}fmt.Println(a2)// 3.指针声明var a3 *Stu = &Stu{"龙丹丹", 18}fmt.Println(a3, *a3)var a4 *Stu = &Stu{Name:"谢思肖",Age:21,}fmt.Println(a4, *a4)
}

1.5使用包导入结构体

不同包之间引用

  1. 首选要大写结构体
  2. 在调用的函数中导入包
文件:stu.go
package mystudy
// 这里要大写大写
type Stu struct{name stringage int
}
/// 调用文件main.go
package main
import "模块名称/mystudy"
func main(){var st mystudy.Stu = mystudy.Stu{"lisi"22}
}

方案2:针对小写的结构体如何对外使用呢,这里借助一个桥梁,就是对外开放的函数
在这里插入图片描述

  1. 首先定义结构体
  2. 定义一个外部可以访问的函数
  3. 访问函数以修改结构体
    定义 mystudy.go
package mystudy
// 面向对象 结构体之方法
import "fmt"type teacher struct{Name stringage int
}func InitTeacher(name string, age int)*teacher{return &teacher{name, age}
}func DemoObjStruct(){var te *teacherte = &teacher{"wagn", 22}fmt.Println(te)(*te).Name = "www"fmt.Println(te)
}

定义主函数

package mainimport ("fmt""com.gdzs/goproject/src/com/gdzs/mystudy"
)
func main(){te := mystudy.InitTeacher("青青", 28)fmt.Println(te)(*te).Name = "qingqing"// 也可以写成 te.Name = "qingqing"fmt.Println(te)
}// 输出结果
&{青青 28}
&{qingqing 28}

注意

  1. 以上的teacher的属性需要首字母大写,主函数中才可以使用te.Name修改,如果是小写则无法访问,就会报如下错误
main.go:86:8: (*te).name undefined (type mystudy.teacher has no field or method name)
  1. 注意声明的时候,不能使用var xxx mystudy.teacher,因为这个结构体是不对外使用的,所以只能使用xxx:=xx这种隐式声明类型的方式。

1.6结构体嵌入

1.6.1 嵌入方式1:匿名结构体

结构体嵌入(Struct embedding)是一种在一个结构体中嵌入另一个结构体的方式,以便通过嵌入的结构体来访问其字段和方法。

通过结构体嵌入,可以将一个结构体类型作为另一个结构体的字段,使得被嵌入的结构体的字段和方法成为了外部结构体的一部分,就好像它们是直接定义在外部结构体中一样。

使用结构体嵌入的主要目的是实现代码的重用和组合。通过嵌入结构体,可以将通用的字段和方法提取到一个单独的结构体中,并在其他结构体中嵌入该结构体,从而实现代码的复用,并且可以通过外部结构体访问嵌入结构体的字段和方法。

package mystudy
// 结构体嵌入知识点
import "fmt"
type Animal struct{Name stringAge int
}func getAnimal(name string, age int)*Animal{return &Animal{name, age}
}func(a Animal) eat(){fmt.Println(a.Name, "吃东西")
}type Dog struct{Animal // 嵌入结构体weight int 
}func (d Dog) call(){fmt.Println(d.Name,"叫了一声,说她的体重是:",d.weight)
}
func (d *Dog) setWight(weight int){(*d).weight = weightfmt.Println(d.weight)
}func DemoQianru(){// 声明方式1var d Dog = Dog{Animal{"花花", 22}, 108}fmt.Println(d)// 声明方式2// 在 Go 语言中,结构体的字段初始化可以按照顺序进行赋值。// 但是在嵌套结构体中,如果嵌入的结构体没有指定字段名,// 那么在初始化时不能省略字段名。var d2 Dog = Dog{Animal:Animal{"兰兰", 22}, weight:108}fmt.Println(d2)d.setWight(99)fmt.Println(d)// 声明方式3使用指针var d3 *Dog = &Dog{Animal:Animal{"雨雨", 22}, weight:108}(*d3).setWight(98)fmt.Println((*d3))
}

嵌入的结构体可以通过变量直接调用,如上代码所示

// 访问方式1
d2.weight
// 访问方式2
d2.Animal.Name

对于指针,这几种方式调用都是可以使得

对于指针,这几种方式调用都是可以使得s
fmt.Println(d3.Animal.Name)
fmt.Println(d3.Name)
fmt.Println((*d3).Name)

1.7.2结构体嵌入:当作一个字段

  1. 一个结构体当作一个类型来嵌入结构体中
type Dog struct {  a Animal // Animal结构体被嵌入到Dog结构体中  
}  
  1. 声明和调用方法
    1. 声明:var c1 = cat{Animal{“猫猫”, 19}, 180}
    2. 调用方式:c1.a.XXX
package mystudy
// 结构体嵌入知识点
import "fmt"
type Animal struct{Name stringAge int
}func getAnimal(name string, age int)*Animal{return &Animal{name, age}
}func(a Animal) eat(){fmt.Println(a.Name, "吃东西")
}type cat struct{a Animalhigh int 
}func DemoQianru(){fmt.Println("------结构体嵌入,当作类型------")// 声明var c1 = cat{Animal{"猫猫", 19}, 180}fmt.Println(c1)// 此语句无法调用,会报错// 16.结构体嵌入.go:59:17: c1.Name undefined (type cat has no field or method Name)// fmt.Println(c1.Name)// 使用以下带调用fmt.Println(c1.a.Name)
}

通过以上如果想通过类来实现的话,还得是使用嵌入的匿名结构体,因为它会自动搜索结构体中的属性的。不需要指定具体的属性

1.7面向对象-封装

有了1.6章节的嵌入结构体可以看到要是实现封装的话,使用嵌入匿名的结构体可以完成。

1.7.1给她创建一个类吧

在 Go 语言中,没有类的概念,而是使用结构体(struct)和方法(method)来实现面向对象的编程。
可以通过定义一个结构体来表示一个类型,并在该结构体上定义方法来操作和处理该类型的实例。这种结构体和方法的组合可以用来模拟类的概念。

下面是一个创建结构体和方法的示例:
(1)首字母大写,其他包可以访问,否则,只能在当前包可以访问
(2)属性也是一样的,大写首字母可以对外访问,否则只能当前包可以访问
(3)方法也是如此:方法使用结构体绑定方法

  1. 定义一个类
package mystudy
// 类知识
type Student struct{Name stringage int
}
  1. 创建构造函数:因为结构体都是值传递,所以这里使用指针
func getStudent(name string, age int)*Student{return &Student(name, age)
}
  1. 创建方法:使用结构体绑定方法
    注意这里设置需要传入指针,才能改变对应的值
func (s *Student)setName(name string){(*s).Nmae = name
}func (s Student)getName(){return s.Name
}

1.7.2类的继承

类继承:使用嵌入匿名结构体即可
(1)实现父类结构体,实现子类结构体,子类结构体嵌入匿名父类结构体
(2)同样定义了showInfo()方法 必须参数和返回值都一样,不能出现参数不同,返回值不同的同名函数,否则会报错
(3)当调用的时候会先在子类中寻找方法或者属性,如果找不到再调用父类的

// 定义父类结构体
type People struct{Name stringAge int
}
// 定义父类方法
func (p People)showInfo()string{fmt.Println("父类方法showInfo")message := fmt.Sprinf("我的名字:%v 我的年龄",p.Name, p.Age)return message
}
// 定义父类方法
func (p People)whoami(){fmt.Println("我是谁:", p.Name)
}
-------------------------------
// 定义子类结构体
type Student struct{Peoplenotes string
}
// 定义子类发方法
func(s Student) showInfo()string{fmt.Println("子类方法showInfo")message := fmt.Sprinf("我的名字:%v 我的年龄",s.Name, s.Age)return message
}

完整的测试代码

package mystudy
// 面向对象-继承
import "fmt"// 定义父类结构体
type People struct{Name stringAge int
}
func (p *People)setName(name string){(*p).Name = name
}// 定义父类方法
func (p People)showInfo()string{fmt.Println("父类方法showInfo")message := fmt.Sprintf("我的名字:%v 我的年龄:%v",p.Name, p.Age)return message
}
// 定义父类方法
func (p People)whoami(){fmt.Println("我是谁:", p.Name)
}// 定义子类结构体
type Student struct{Peoplenotes string
}
// 定义子类发方法
func(s Student) showInfo()(message string){// 子类的方法如果和父类同名,那么就必须参数和返回值都是一样的,否则就会报错fmt.Println("子类方法showInfo")message = fmt.Sprintf("我的名字:%v 我的年龄:%v", s.Name, s.Age)fmt.Println(message)return 
}func DemoJc(){// 声明一个对象fmt.Println("------面向对象-继承------")var s1 = Student{People{"关关", 19}, "前任2"}message := s1.showInfo()fmt.Println(message)fmt.Println("==重新设置新的名字:盈盈")s1.setName("颖颖")message = s1.showInfo()fmt.Println(message)
}
// 输出结果
------面向对象-继承------
子类方法showInfo
我的名字:关关 我的年龄:19
我的名字:关关 我的年龄:19
==重新设置新的名字:盈盈
子类方法showInfo
我的名字:颖颖 我的年龄:19
我的名字:颖颖 我的年龄:19

1.7.3多继承

多继承就是按照1.7.2的介绍,这个时候多加几个嵌入结构体就行了

type People struct{Name stringAge int
}type People2 struct{Name stringAge inthigi int
}type Student struct{People People2notes string
}func DemoMain(){var st = Student{People{"xxx", 19}, People2{"xxx", 21, 180}, "测试"}
}

完整代码

package mystudy
// 面向对象-继承
import "fmt"/*
人父类
*/
// 定义父类结构体
type People struct{Name stringAge int
}func (p *People)setName(name string){(*p).Name = name
}// 定义父类方法
func (p People)showInfo()string{fmt.Println("父类方法showInfo")message := fmt.Sprintf("我的名字:%v 我的年龄:%v",p.Name, p.Age)return message
}
// 定义父类方法
func (p People)whoami(){fmt.Println("我是谁:", p.Name)
}/*
父类:People2
*/
type People2 struct{Name stringAge inthigh int
}/*
学生子类
*/// 定义子类结构体
type Student struct{PeoplePeople2notes string
}
// 定义子类发方法
func(s Student) showInfo()(message string){// 子类的方法如果和父类同名,那么就必须参数和返回值都是一样的,否则就会报错fmt.Println("子类方法showInfo")message = fmt.Sprintf("我的名字:%v 我的年龄:%v", s.People.Name, s.People2.Age)fmt.Println(message)return 
}func DemoJc(){// 声明一个对象fmt.Println("------面向对象-继承------")var s1 = Student{People{"关关", 19},People2{"关关2", 20, 88}, "前任2"}message := s1.showInfo()fmt.Println(message)fmt.Println("==重新设置新的名字:盈盈")s1.setName("颖颖")message = s1.showInfo()fmt.Println(message)
}
//
------面向对象-继承------
子类方法showInfo
我的名字:关关 我的年龄:20
我的名字:关关 我的年龄:20
==重新设置新的名字:盈盈
子类方法showInfo
我的名字:颖颖 我的年龄:20
我的名字:颖颖 我的年龄:20

注意:

  1. 如果父类的属性有重复的,这个时候调用属性的时候要指定具体的结构体
s1.People.Name
s1.People2.Age

1.7.4注意事项

  1. 结构体的嵌入匿名结构体可以是基本类型
type People struct{Name stringAge int
}type Studen struct{Peopleintfloat32
}st2 := Student2{People{"学生2", 22}, 18,19}
fmt.Println(st2)
fmt.Println(st2.int)
fmt.Println(st2.float32)
// 输出结果
{{学生2 22} 18 19}
18
19
  1. 创建结构体的时候可以使用之前的声明方法,使用key或者直接按照顺序
st3 := Student2{People:People{Name:"学生3",Age:22,},int:19,float32:99,
}
fmt.Println(st3)
注意,最后要加上 逗号
  1. 对于指针结构体嵌入引用的也是类似2的声明
package mystudy
// 面向对象-继承
import "fmt"type Student3 struct{*Peopleint
}func DemoJc(){st4 := Student3{&People{"李华", 22}, 88,}fmt.Println(st4)fmt.Println(st4.People)fmt.Println(*(st4.People))
}
// 输出结果
{0xc000116030 88}
&{李华 22}
{李华 22}
  1. 结构体的字段可以是结构体的类型,这个在1.6章节介绍了。这通常也称为组合模式
type People struct{Name string
}type Student struct{p People
}

1.8实现接口

1.8.1语法定义

  1. 接口语法:接口只能定义方法,不能定义变量
type Persion interface{// 方法eat()// 方法say()
}
  1. 继承接口
type Student struct{}
// 实现方法
func(s Student) eat(){fmt.Println("eat")
}

1.8.2注意事项

  1. 接口中定义一组方法,但是不需要实现,不需要方法体,接口不能包含任何变啊零。
  2. 实现接口要实现所有的方法才是实现,一下代码没有全部实现会报错
type Persion1 interface {SayHello()eat()
}
// 定义学生类
type StudentClass01 struct{Name string
}func(s StudentClass01) SayHello(){fmt.Println(s, "Student say hello")
}当你调用的时候这里就会报错,如果没有全部实现编译不会报错
var st StudentClass01
var p Persion1
p = st
p.SayHello()goproject\src\com\gdzs\mystudy\18.接口01.go:33:5: cannot use st (variable of type StudentClass01) as Persion1 value in assignment: StudentClass01 does not implement Persion1 (missing method SayHello)
  1. Golang中的接口不需要显式的实现接口,Golang中没有implement关键字(Go中实现接口是基于方法的,不是基于接口的)
  2. 如果有两个接口 A B 有共同的方法 a b 如果c结构体实现了a b两个方法,那么就是两个接口都实现了 如下的Persion1 和Persion两个接口
package mystudy
// 接口定义
import "fmt"type Persion interface {SayHello()
}type Persion1 interface {SayHello()
}
// 定义学生类
type StudentClass01 struct{Name string
}func(s StudentClass01) SayHello(){fmt.Println(s, "Student say hello")
}
// 定义教师类
type Teacher struct{
}func(t Teacher) SayHello(){fmt.Println(t, "Teacher say hello")
}func say(p Persion1){p.SayHello()
}func DemoInterface01(){var st StudentClass01var t Teachersay(st)say(t)
}
  1. 接口本身不能创建实例,但是可以指向一个实现了该接口的自定义类型的变量:这里说的其实就是面向对象的多态
// 定义教师类
type Teacher struct{
}func(t Teacher) SayHello(){fmt.Println(t, "Teacher say hello")
}func say(p Persion1){p.SayHello()
}func DemoInterface01(){var t Teachersay(t)
}
  1. 只要是自定义数据类型,就可以实现接口,不仅仅是结构体类型
type Persion1 interface{SayHello()
}func say(p Persion1){p.SayHello()
}
// 其他类型的接口
type integer intfunc (i integer)SayHello(){fmt.Println("int say hello:", i)
}func DemoInterface01(){var i integer =100say(i)
}
// 打印结果
int say hello:100
  1. 一个自定义类型可以实现多个接口:同一个模块下的接口可以直接使用,比如这里的Persion使用的就是前面代码模块里面的
package mystudy
import "fmt"
// 实现多个接口
// 接口1
type Persion2 interface{eat()
}
// 接口2
type Animal2 interface{say()
}
// 结构体实现
type Student6 struct{}
// 实现接口1
func(s Student6)eat(){fmt.Println("student eat")
}
// 实现接口2
func(s Student6)say(){fmt.Println("student say")
}// 实现接口2
func(s Student6) SayHello(){fmt.Println("Student say hello")
}
func DemoMulInterface(){var st Student6var p2 Persion2// 前面代码模块里面的var p1 Persionvar a Animal2p2 = sta = stp1 = stp2.eat()a.say()p1.SayHello()
}
// 输出结果
student eat
student say
Student say hello
  1. 接口可以继承别的接口,A继承B ,那么实现的时候要都实现所有接口的方法才可以
    #GO接口继承
package mystudy
// 接口继承接口
import "fmt"type Ainter interface{a()
}
type Binter interface{b()
}
type Cinter interface{AinterBinterc()	
}type Dstruct struct{}func (s Dstruct) a(){fmt.Println("aaa")
}
func (s Dstruct) b(){fmt.Println("bbb")
}
func (s Dstruct) c(){fmt.Println("ccc")
}func DemoMulInterInter(){var s Dstructvar c Cinter = sc.c()
}
  1. 接口类型默认是一个指针类型,没有对应接口初始化,那么就会输出空
    在这里插入图片描述

  2. 空接口时所有类型的祖先,因为所有类型都实现了空接口, 可以把任何一个变量赋给空接口
    #GO空接口
    如下的空接口

type Empty interface{}
// 空接口可以接收任意一个变量
var a interface{} = 12
var b interface{} = "aa"
var c interface{} = 'x'

在这里插入图片描述

1.9多态

多态(Polymorphism)是一种面向对象编程的重要概念,它允许我们使用统一的接口来处理不同类型的对象。Go语言通过接口(Interface)来实现多态性。
代码示例:

package mystudy
import "fmt"
// 多态
type Shape interface {Area() float64
}/*
定义矩形类型
*/
type Rectangle struct{Width float64Height float64
}
// 实现方法
func (r Rectangle) Area() float64 {return r.Width * r.Height
}
// 特定方法
func (r Rectangle) GetHeight() float64{return r.Height
}
/*
定义圆形
*/
type Circle struct{Radius float64
}
// 定义圆形面积
func (c Circle) Area() float64{return 3.14 * c.Radius * c.Radius
}// 公共方法
func DtTest(s Shape) {fmt.Println("公共方法:", s.Area())//因为GetHeight是矩形类独有的,所以这里需要转类型//这里使用断言 这个方法接收会出现报错,程序不会继续执行// var re Rectangle = s.(Rectangle)// 断言方法2re, ok := s.(Rectangle)fmt.Println(ok)fmt.Println(re.GetHeight())
}func DtDemo(){shape := []Shape{Circle{10},Rectangle{10, 11},}	for i, shape := range shape{fmt.Printf("%T:", shape)fmt.Println(i, shape.Area())DtTest(shape)switch shape.(type){case Rectangle:fmt.Println("这是矩形")case Circle:fmt.Println("这是圆形")}}
}
  1. 多态时需要使用类型判定方法
    s.(Rectangle)判断类型并强制转换类型为Rectangle 这里会返回两个值,re,ok ok是bool表示是否转换成功。re是转成功后的 类型可以调用独有的方法
// 公共方法
func DtTest(s Shape) {fmt.Println("公共方法:", s.Area())//因为GetHeight是矩形类独有的,所以这里需要转类型//这里使用断言 这个方法接收会出现报错,程序不会继续执行// var re Rectangle = s.(Rectangle)// 断言方法2re, ok := s.(Rectangle)fmt.Println(ok)fmt.Println(re.GetHeight())
}
  1. 方法2:不推荐这种方法,因为会报错
var s = s.(Rectangle)
s.GetHeight()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/304897.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

day55 最长递增子序列 最长连续递增子序列 最长重复子数组

题目1 300 最长递增子序列 题目链接 300 最长递增子序列 题意 找到整数数组nums的最长严格递增子序列的长度&#xff08;子序列并不改变原始的顺序&#xff0c;但是可以删除元素&#xff09; 动态规划 动规五部曲 1&#xff09;dp数组及下标i的含义 dp[i] 表示以nums[i…

SpringMVC数据接收(全面/详细注释)

SpringMVC涉及组件&#xff1a; DispatcherServlet : SpringMVC提供&#xff0c;我们需要使用web.xml配置使其生效&#xff0c;它是整个流程处理的核心&#xff0c;所有请求都经过它的处理和分发&#xff01;[ CEO ]HandlerMapping : SpringMVC提供&#xff0c;我们需要进行…

OSCP靶场--Dibble

OSCP靶场–Dibble 考点(前端鉴权参数修改node.js代码注入 suid cp提权 ) 1.nmap扫描 ## ┌──(root㉿kali)-[~/Desktop] └─# nmap 192.168.173.110 -sV -sC -Pn --min-rate 2500 -p- Starting Nmap 7.92 ( https://nmap.org ) at 2024-04-09 06:36 EDT Nmap scan repor…

使用yolov8实现自动车牌识别(教程+代码)

该项目利用了一个被标记为“YOLOv8”的目标检测模型&#xff0c;专门针对车牌识别任务进行训练和优化。整个系统通常分为以下几个核心步骤&#xff1a; 数据准备&#xff1a; 收集包含车牌的大量图片&#xff0c;并精确地标记车牌的位置和文本信息。数据集可能包含各种环境下的…

MyLife 使用 TianliGPT 自动生成文章的AI摘要

博客还未迁移的时候&#xff0c;文章摘要就是使用 TianliGPT 自动生成的&#xff0c;现在迁移到 MyLife主题 后&#xff0c;特此记录一下。 前言 此教程的前提需要阅读 张洪Heo 的文章&#xff1a;如何让博客支持AI摘要&#xff0c;使用TianliGPT自动生成文章的AI摘要 购买 Ti…

探索 ChatGPT:解读 AI 对话的魔力(文末推荐一款AI工具聚合平台,可免费体验)

&#x1f947;作者简介&#xff1a;CSDN内容合伙人、新星计划第三季Python赛道Top1 &#x1f525;个人主页&#xff1a;hacker707的csdn博客 &#x1f4ac;推荐一款AI工具聚合平台&#x1f449;Hulu AI 探索 ChatGPT&#xff1a;解读 AI 对话的魔力 ChatGPT 的魅力如何使用 C…

Linux系统本地搭建DbGate数据库并结合内网穿透实现无公网IP远程连接

文章目录 1. 安装Docker2. 使用Docker拉取DbGate镜像3. 创建并启动DbGate容器4. 本地连接测试5. 公网远程访问本地DbGate容器5.1 内网穿透工具安装5.2 创建远程连接公网地址5.3 使用固定公网地址远程访问 本文主要介绍如何在Linux Ubuntu系统中使用Docker部署DbGate数据库管理工…

ios苹果ipa文件app内测分发有哪些操作流程

哈喽&#xff0c;大家好&#xff0c;咕噜淼淼又来和大家见面啦&#xff0c;在iOS应用开发过程中&#xff0c;进行内测分发是非常重要的一环&#xff0c;它能帮助开发者发现并修复应用中的问题&#xff0c;提升用户体验。上两期咱们一起探讨了一下App内测分发的目的及优势&#…

海山数据库(He3DB)原理剖析:浅析OLAP数据库计算引擎中的统计信息

背景&#xff1a; 统计信息在计算引擎的优化器模块中经常被提及&#xff0c;尤其是在基于成本成本优化&#xff08;CBO&#xff09;框架中统计信息发挥着至关重要的作用。CBO旨在通过评估执行查询的可能方法&#xff0c;并选择最有效的执行计划来提高查询性能。而统计信息则提…

深入OceanBase内部机制:系统架构与组件精讲

码到三十五 &#xff1a; 个人主页 心中有诗画&#xff0c;指尖舞代码&#xff0c;目光览世界&#xff0c;步履越千山&#xff0c;人间尽值得 ! 目录 1️⃣OceanBase 整体架构1.1 分区1.2 分片1.3 日志流1.4 对等节点1.5 多租户 2️⃣OceanBase 架构与组件详解2.1 存储层2.2 …

备考ICA----Istio实验18---单集群中部署多个Istio控制面

备考ICA----Istio实验18—单集群中部署多个Istio控制面 单个 Kubernetes 控制面以及多个 Istio 控制面和多个网格。通过 Kubernetes 命名空间和 RBAC 实现软多租户业务隔离。 1. 环境准备 1.1 创建2个命名空间 kubectl create ns usergroup-1 kubectl label ns usergroup-…

头歌-机器学习 第16次实验 EM算法

第1关:极大似然估计 任务描述 本关任务:根据本节课所学知识完成本关所设置的选择题。 相关知识 为了完成本关任务,你需要掌握: 什么是极大似然估计; 极大似然估计的原理; 极大似然估计的计算方法。 什么是极大似然估计 没有接触过或者没有听过”极大似然估计“的同学…

vue商城项目vue shop vite

Vue Shop 是一个基于 Vue.js 框架构建的电子商务平台&#xff0c;它利用了 Vue 的响应式数据绑定和组件化的特点&#xff0c;为用户提供了一种快速开发和部署在线商店的解决方案。Vite 是一种现代化的前端构建工具&#xff0c;它提供了快速的冷启动、即时模块热更新&#xff08…

篮球竞赛|基于Springboot的篮球竞赛预约平台系统设计与实现(源码+数据库+文档)

篮球竞赛预约平台目录 基于Springboot的篮球竞赛预约平台系统设计与实现 一、前言 二、系统设计 三、系统功能设计 1、前台&#xff1a; 2、后台 管理员功能 用户功能 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff…

Jackson配置处理LocalDateTime、LocalDate等java8时间类型失效的问题解决

目录 前言 一、问题排查过程 1.1 SpringMvc是如何处理请求报文和响应报文 1.2 JacksonConfig配置排查 二、导致Jackson配置失效的原因 2.1 没有addSerializer 2.2 添加了EnableMvc注解 2.3 另外有地方配置了Jacksonhttpconver覆盖了配置 总结 前言 上一篇文章《使用Ja…

【matlab】如何解决打开缓慢问题(如何让matlab在十几秒内打开)

【matlab】如何解决打开缓慢问题&#xff08;如何让matlab在十几秒内打开&#xff09; 找到我们解压缩时Crack中的license_standalone.lic文件&#xff0c;将其拷贝 在安装matlab的路径下新建一个文件&#xff0c;粘贴上面的license_standalone.lic文件 在桌面鼠标移动到matl…

【Linux系列】如何确定当前运行的是 RHEL 9 还是 RHEL 8?

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

lua学习笔记13(一些特殊用法的学习和三目运算符的实现)

print("*****************************一些特殊用法的学习*******************************") print("*****************************多变量赋值*******************************") local a,b,c114514,8848,"凌少" print(a) print(b) print(c) -…

DAS-MIL

DAS-MIL论文笔记 题目&#xff1a;DAS-MIL: Distilling Across Scales for MIL Classification of Histological WSIs 摘要 近年来&#xff0c;采用多实例学习 &#xff08;MIL&#xff09; 对全玻片图像 &#xff08;WSI&#xff09; 进行分类的情况有所增加。事实上&#…

VUE3的有关知识

学习vue3的原因 在vue2当中的组件的实例,都是data一块,computed一块,当我们去找某一变量相关的则十分麻烦,vue3是组合式API,vue2是选项式, vue3的优点: 1)组合式更易维护 2)更快的速度 3)更小的体积 4)更好的响应式proxy 使用vue3相关脚手架创建项目 步骤: 1)node -v node版…