Go语言结构体、方法与接口

news/2024/11/8 22:25:43/

文章目录

  • 一、结构体构造函数
      • Go语言中的构造函数语法
  • 二、结构体方法和接收器
      • 无参数和返回值
      • 值类型接收者
      • 指针类型接收者
        • 方法继承
        • 方法重写
  • 三、结构体比较
      • 结构体比较要求
      • 结构体比较符号
  • 四、接口声明
      • 接口定义
      • 接口特点
      • 接口格式
      • 标准格式接口的实现:
      • 空接口
      • error接口
  • 五、接口与结构体
      • 接口与结构体的关系
      • 一个结构体类型实现多个接口
      • 一个接口被多个结构体实现
  • 六、接口的嵌套
      • 接口嵌套的定义
      • 接口嵌套的格式

一、结构体构造函数

Go语言中的构造函数:

  • Go 语言没有构造函数的功能
  • 使用结构体初始化的过程来模拟实现构造函数
  • 结构体可以像其他数据类型一样将结构体类型作为参数传递给函数

在这里插入图片描述

Go语言中的构造函数语法

type 类型名 struct {字段名1 字段类型1字段名2 字段类型2......
}
func newfunc(变量名1 变量类型, 变量名2 变量类型 ……) *类型名{return &类型名{字段名1: 变量1,字段名2: 变量2......}
}
  • Go语言的类型或结构体没有构造函数的功能,但是我们可以使用结构体初始化的过程来模拟实现构造函数。
  • 构造函数传入的变量类型需要与return返回结构体指针字段名类型一致
  • 构造函数可以只返回结构体中某些字段名

示例1:

package main
import "fmt"
type person struct {name stringage  int
}
//构造函数约定俗成用new开头,不是强制
//返回的是结构体还是结构体指针
//当结构体比较大的时候使用结构体指针,减少程序内存的开销。
func personOnlyName(name string) *person {return &person{name: name,}//仅返回部分字段
}func newperson(name string, age int) *person {return &person{name: name,age:  age,}//返回所有字段
}
func main()  {p1 := personOnlyName("Go语言")fmt.Println(p1)p2 := newperson("golang", 20)fmt.Println(p2)
}//运行结果:
//&{Go语言 0}
//&{golang 20}

示例2:

package main
import "fmt"
type person struct {name stringage  int
}
func print1(p person){p.name="值传递"fmt.Println("print1",p)
}
func print2(p *person){p.name="指针传递"fmt.Println("print2",p)
}func main()  {p1:=person{"Go语言",20}print1(p1)	      //值传递,形参无法改实参fmt.Println(p1)print2(&p1)  //指针传递,形参可以改实参fmt.Println(p1)
}//运行结果:
//print1 {值传递 20}
//{Go语言 20}
//print2 &{指针传递 20}
//{指针传递 20}

二、结构体方法和接收器

Go语言同时有函数和方法,方法的本质是函数,但是方法和函数又具有不同点。
在这里插入图片描述在这里插入图片描述

无参数和返回值

func (结构体变量 结构体类型) 方法名() {函数体
}

值类型接收者

func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) {函数体
}

指针类型接收者

func (接收者变量 *接收者类型) 方法名(参数列表) (返回参数) {函数体
}

示例:

package main
import "fmt"
type person struct {name stringage  int
}
func (p person)print(){  //无参数和返回值fmt.Println(p)
}
func (p person)change1(s string){p.name=s  //值类型接收者
}
func (p *person)change2(s string){p.name=s   //指针类型接收者
}func main()  {p := person{"Go",20}p.print()p.change1("Golang")p.print()p.change2("Go语言")p.print()
}//运行结果为:
//{Go 20}
//{Go 20}
//{Go语言 20}

若方法的接受者不是指针,实际只是获取了一个copy,而不能真正改变接受者中原来的数据。

使用方法的原因:

  • 既然可以用函数来写相同的程序,却还要使用方法,主要有以下两个原因
  • 如Go不是一种纯粹面向对象的编程语言,它不支持类。因此其方法是一种实现类似于类的行为的方法。
  • 如果相同名称的方法可以在不同的类型上定义,而具有相同名称的函数是不允许的。假设有一个正方形和圆形的结构。可以在正方形和圆形上定义一个名为Area的求取面积的方法。
  • 下面通过一个示例来观察不同的结构体中方法名。

示例:

package main
import ("math""fmt"
)
type Rectangle struct {width, height float64
}
type Circle struct {radius float64
}
func main() {r1 := Rectangle{10, 4}r2 := Rectangle{12, 5}c1 := Circle{1}c2 := Circle{10}    fmt.Println("r1 的面积", r1.Area())fmt.Println("r2 的面积", r2.Area())fmt.Println("c1 的面积", c1.Area())fmt.Println("c2 的面积", c2.Area())
}
func (r Rectangle) Area() float64 {return r.width * r.height
}   // 定义 Rectangle 的方法
func (c Circle) Area() float64 {return c.radius * c.radius * math.Pi
}   // 定义 Circle 的方法//运行结果为:r1 的面积 40
//r2 的面积 60
//c1 的面积 3.141592653589793
//c2 的面积 314.1592653589793
方法继承
  • 方法是可以继承的,如果匿名字段实现了一个方法,那么包含这个匿名字段的struct也能调用该匿名结构体中的方法。
方法重写
  • 方法是可以继承和重写的。

示例:

package main
import "fmt"
type Human struct { name  stringphone stringage   int
}
type Student struct { // 继承HumanHuman  // 嵌入Human,匿名字段school string
} 
type Employee struct {Human   // 匿名字段company string
}  // Human的SayHi方法
func (h Human) SayHi() {fmt.Printf("大家好! 我是%s,%d岁,联系方式是: %s\n", h.name, h.age, h.phone)
}  
func main() {s1 := Student{Human
{"Daniel", "15012345678", 13}, "十一中学"}e1 := Employee{Human
{"Steven", "17812345678", 35}, "1000phone"}s1.SayHi()e1.SayHi()
}
package main
import "fmt"
type Human struct {name  stringphone stringage   int
}
type Student struct {Human  //继承Humanschool string
}
type Employee struct {Human  //继承Humancompany string
}
func (h Human) SayHi() {
fmt.Printf("大家好! 我是%s,%d岁,联系方式是: %s\n", h.name, h.age, h.phone)
}
func (s Student) SayHi() {
fmt.Printf("大家好! 我是%s,%d岁,我在%s上学,联系方式是: %s\n", s.name, s.age, s.school, s.phone)
}
func (e Employee) SayHi() {
fmt.Printf("大家好! 我是%s,%d岁,我在%s工作,联系方式是: %s\n", e.name, e.age, e.company, e.phone)  }
func main() {s1 := Student{Human{"Daniel", "15012345678", 13}, "十一中学"}e1 := Employee{Human{"Steven", "17812345678", 35}, "1000phone"}s1.SayHi() // 调用各自的SayHi方法e1.SayHi()
}

三、结构体比较

结构体比较要求

  • 结构体可以比较,也不可以比较
  • 如果结构体的全部成员都是可以比较的,那么结构体也是可以比较的
  • 如果包含slice、map、function不可比较类型时,那么结构体是不可以比较的

结构体比较符号

  • 可以比较的情况下:两个结构体将可以使用**==或!=运算符进行比较,但不支持>或<**
  • 同类型的两个结构体变量可以相互赋值

示例1:

package main
import "fmt"
type person struct {name stringage  int
}
func main()  {p1 :=person{"Go语言",20}p2 :=person{"Go语言",20}if p1==p2{fmt.Println("p1=p2")}else {fmt.Println("p1!=p2")}
}//运行结果为:
//p1=p2

示例2:

package main
import "fmt"
type person struct {name stringage  []int
}
func main()  {p1 :=person{"Go语言",[]int{20}}p2 :=person{"Go语言",[]int{20}}if p1==p2{fmt.Println("p1=p2")}else {fmt.Println("p1!=p2")}
}//报错提示:
//invalid operation: p1 == p2 (struct containing []int cannot be compared)

!! 不能比较包含[]int的结构体

示例3:

package main
import "fmt"
type person1 struct {name stringage  int
}
type person2 struct {name stringage  int
}
func main()  {p1 :=person1{"Go语言",20}p2 :=person2{"Go语言",20}if p1==p2{fmt.Println("p1=p2")}else {fmt.Println("p1!=p2")}
}//报错提示:
//invalid operation: p1 == p2 (mismatched types person1 and person2)

!! 结构体不同则不能比较

示例4:

package main
import "fmt"
type person1 struct {name stringage  int
}
type person2 struct {name stringage  int
}
func main()  {p1 :=person1{"Go语言",20}p2 :=person2{"Go语言",20}if p1==person1(p2){fmt.Println("p1=p2")}else {fmt.Println("p1!=p2")}
}//运行结果:
//p1=p2

结构体内部相同时,可以使用强制转换来实现比较

四、接口声明

接口

  • 面向对象语言中,接口用于定义对象的行为。接口只指定对象应该做什么,实现这种行为的方法(实现细节)是由对象来决定。
  • 接口定义了一组方法,如果某个对象实现了该接口的所有方法,则此对象就实现了该接口。

Go语言是一种“鸭子类型”的语言

  • Go没有 implements, extends 关键字,其实这种编程语言叫做duck typing编程语言。
  • duck typing是描述事物的外部行为而非内部结构。在“鸭子类型”中,关注的不是对象的类型本身,而是它是如何使用的。

接口定义

  • 接口是对类型行为的约定
  • 接口是一系列方法的集合
  • 接口是一种高度抽象的数据类型
  • 接口中的方法不包含代码
  • 接口中的方法是抽象的
  • 接口中不包含变量
  • 本质是一种关于对象功能的约定

接口特点

  • 接口是双方约定的一种合作协议,接口实现者不需要关心接口会怎样被使用,只需要实现接口里面所有的方法即可
  • 接口不支持直接实例化,只能通过具体的类来实现声明的所有方法,同时函数的函数名、函数参数和函数返回值必须完全一样
  • Go语言中的接口支持赋值操作,从而快速实现接口与实现类的映射

接口格式

type 接口类型名 interface{方法名1( 参数列表1 ) 返回值列表1方法名2( 参数列表2 ) 返回值列表2......
}

接口要点:

  • 接口类型名:使用 type 将接口定义为自定义的类型名,接口在命名时,一般会在单词后面添加er;
  • 方法名:当方法名首字母是大写时,同时接口类型名首字母也是大写时,这个方法可以被接口所在的包(package)之外的代码访问;
  • 参数列表:表示传入到方法中的值;
  • 返回值列表:方法返回值。
    示例:
type Phone interface {call()SendMsg(msg string) bool
}

标准格式接口的实现:

type 接口类型名 interface{方法名1( 参数列表1 ) 返回值列表1方法名2( 参数列表2 ) 返回值列表2......
}
type (变量名 类型) 方法名1( 参数列表1 ) 返回值列表1{
}
type (变量名 类型) 方法名2( 参数列表2 ) 返回值列表2{
}

示例:

//标准格式接口的实现案列
type AndroidPhone struct {
}  //实现接口的结构体func (a AndroidPhone) call() {fmt.Println("AndroidPhone calling")
}func (a AndroidPhone) SendMsg(msg string) bool {fmt.Println("AndroidPhone sending msg")return true
}

空接口

  • 是接口类型的特殊形式
  • 空接口没有任何方法,因此任何类型都无须实现空接口
  • 从实现的角度看,任何值都满足这个接口的需求
  • 空接口类型可以保存任何值,也可以从空接口中取出原值

示例:

package main
import "fmt"
func main() {var any interface{}any = 1fmt.Println(any)any = "hello"fmt.Println(any)any = falsefmt.Println(any)
}//运行结果为:
//1
//hello
//false

error接口

  • 作为错误处理的标准模式
  • 如果函数要返回错误,则返回值类型列表中肯定包含 error
  • error 处理过程类似于C语言中的错误码,可逐层返回,直到被处理

示例:

package main
import ("errors""fmt""math"
)
func Sqrt(f float64) (float64, error) {if f < 0 {return -1, errors.New("开平方根的数字不能小于0")}return math.Sqrt(f), nil
}
func main() {result, err := Sqrt(-13)if err != nil {fmt.Println(err)} else {fmt.Println(result)}
}//运行结果为:
//开平方根的数字不能小于0

五、接口与结构体

接口与结构体的关系

在这里插入图片描述

一个结构体类型实现多个接口

格式:

type 接口类型名1 interface{方法名1( 参数列表1 ) 返回值列表1
}
type 接口类型名2 interface{方法名2( 参数列表2 ) 返回值列表2
}type 结构体 struct{字段名1  字段类型1字段名2  字段类型2
}
type (结构体名 结构体) 方法名1( 参数列表1 ) 返回值列表1{
}
type (结构体名 结构体) 方法名2( 参数列表2 ) 返回值列表2{
}

示例:

package main
import "fmt"
type run interface{running(time int)int
}
type eat interface{eating(food string)int
}
type person struct {weight int
}
func (p *person)running(time int)int{p.weight -= time/5return p.weight
}
func (p *person)eating(food string)int{p.weight += 1return p.weight
}
func main() {p1 :=&person{weight: 50,}fmt.Println(p1.weight)p1.running(20)fmt.Println(p1.weight)p1.eating("面包")fmt.Println(p1.weight)//运行结果为:
//50
//46
//47

一个接口被多个结构体实现

格式:

type 接口类型名 interface{方法名1( 参数列表1 ) 返回值列表1方法名2( 参数列表2 ) 返回值列表2
}
type 结构体1 struct{字段名1  字段类型1
}
type 结构体2 struct{字段名2  字段类型2
}
type 结构体3 struct{字段名3 字段类型3
}
type (结构体名1 结构体1) 方法名1( 参数列表1 ) 返回值列表1{
}
type (结构体名2 结构体2) 方法名2( 参数列表2 ) 返回值列表2{
}
type (结构体名3 结构体3) 方法名2( 参数列表2 ) 返回值列表2{
}

示例:

package main
import "fmt"
type who interface{who(position string)string
}
type tearcher struct {position stringname string
}
type student struct {position stringname string
}
func (t *tearcher)who(name string)string{t.position="教师"t.name=namereturn t.position
}
func (s *student)who(name string)string{s.position="学生"s.name=namereturn s.position
}
func main() {var t who=&tearcher{position: "未知",name: "未知",}t.who("Go")fmt.Println(t)var s who=&student{position: "未知",name: "未知",}s.who("go")fmt.Println(s)
}//运行结果为:
//&{教师 Go}
//&{学生 go}

六、接口的嵌套

接口嵌套的定义

在Go 语言中,不仅仅结构体与结构体之间可以嵌套,接口与接口之间也可以嵌套,被包含的接口中的所有方法都会被包含到新的接口中。

接口嵌套的格式

type 接口类型名1 interface{方法名1()
}
type 接口类型名2 interface{方法名2()
}
type 接口类型名3 interface{接口类型名1接口类型名2方法名3()
}

一个接口可以包含一个或多个其他的接口,这相当于直接将这些内嵌接口的方法列举在外层接口中一样。只要接口的所有方法被实现,则这个接口中的所有嵌套接口的方法均可以被调用。

//接口未嵌套
type queue interface {height()
}
type run interface {weight()
}
type BMI interface {height()weight()age()
}
//接口嵌套
type queue interface {height()
}
type run interface {weight()
}
type BMI interface {queuerunage()
}

示例:嵌套接口的实现

package main
import "fmt"
type queue interface {height() int
}
type run interface {weight() int
}
type BMI interface {queue //嵌套接口runage() int
}
type Person struct {  //结构体h, w, a int
}
func (p Person) height() int {return p.h 
}     // 实现queue接口的height方法
func (p Person) weight() int {return p.w
}   
func (p Person) age() int {return p.a
} 
func main() {p := Person{h: 170, w: 65, a: 25}fmt.Println("Height:", p.height())fmt.Println("Weight:", p.weight())fmt.Println("Age:", p.age())
}//运行结果为:
//Height: 170
//Weight: 65
//Age: 25

http://www.ppmy.cn/news/1545469.html

相关文章

基于matlab处理语音信号

matlab有处理语音信号的函数wavread&#xff0c;不过已经过时了&#xff0c;现在处理语音信号的函数名称是audioread选取4.wav进行处理&#xff08;只有4的通道数为1&#xff09; 利用hamming窗设计滤波器 Ham.m function [N,h,H,w] Ham(fp,fs,fc)wp 2*pi*fp/fc;ws 2*pi*…

利用Java easyExcel库实现高效Excel数据处理

在Java应用程序中&#xff0c;处理Excel文件是一项常见任务&#xff0c;尤其是在需要读取、写入或分析大量数据时。easyExcel是一个基于Java的高性能Excel处理库&#xff0c;它提供了简洁的API和优化的性能&#xff0c;以简化Excel文件的处理。本文将指导您如何使用easyExcel库…

(2024最新完整详细版)Docker部署MinIO

对象存储MinIO 对象存储是用于存储非结构化数据的数据存储架构&#xff0c;它将一个数据单元称为一个对象&#xff0c;每个对象都包含数据本身、元数据&#xff08;描述数据的信息&#xff09;和一个唯一标识符&#xff08;通常是一个URL地址&#xff09;。 MinIO是一个开源的对…

金融系统中事务

金融服务行业需要处理大量的交易和请求&#xff0c;Java的多线程能力可以有效地管理这些并发操作&#xff0c;确保系统的响应性和效率。 在金融服务行业中&#xff0c;例如一个股票交易平台&#xff0c;它需要处理大量的买入和卖出请求&#xff0c;交易逻辑会涉及数据库交互、…

《JavaEE进阶》----20.<基于Spring图书管理系统(登录+添加图书)>

PS&#xff1a;关于接口定义 接口定义&#xff0c;通常由服务器提供方来定义。 1.路径&#xff1a;自己定义 2.参数&#xff1a;根据需求考虑&#xff0c;我们这个接口功能完成需要哪些信息。 3.返回结果&#xff1a;考虑我们能为对方提供什么。站在对方角度考虑。 我们使用到的…

集合进阶------泛型(JAVA笔记第三十期)

p.s.这是萌新自己自学总结的笔记&#xff0c;如果想学习得更透彻的话还是请去看大佬的讲解 目录 泛型概念泛型类泛型接口泛型接口的实现类泛型方法可变参数的泛型方法 泛型的通配符 泛型概念 泛型&#xff0c;顾名思义&#xff0c;广泛的类型&#xff0c;使用泛型可以帮助我们…

基于springboot+vue实现的任务管理系统(源码+L文)4-103

第4章 系统设计 4.1 总体功能设计 员工&#xff0c;经理&#xff0c;管理员都需要登录才能进入任务管理系统&#xff0c;使用者登录时会在后台判断使用的权限类型&#xff0c;包括一般使用者和管理者,一般使用者为员工和经理&#xff0c;对员工只能提供任务信息显示查询&…

【JAVA】Java基础—Java开发环境搭建:安装JDK与IDE(如IntelliJ IDEA、Eclipse)

Java是一种强大的编程语言&#xff0c;广泛应用于各种领域&#xff0c;包括企业级应用、移动应用&#xff08;如Android&#xff09;、Web应用和大数据处理等。Java的“编写一次&#xff0c;到处运行”&#xff08;Write Once, Run Anywhere, WORA&#xff09;特性使得它在跨平…