历览千载书,时时见遗烈。这篇文章主要讲述Go 语言入门很简单 -- 12. Go 方法 #私藏项目实操分享#相关的知识,希望能为你提供帮助。
前言
虽然从技术上讲 Go 不是面向对象的编程语言,但类型和方法允许采用面向对象的编程风格。最大的不同是 Go 不支持类型继承,而是有接口的概念。
在本文中,我们将重点介绍 Go 对方法和接口的使用。
Note:一个常见的问题是“函数和方法之间的区别是什么”。方法是具有定义接收器的函数,在 OOP( Object Oriented Programming language) 术语中,方法是对象实例上的函数。
方法
在上一章 ??Go 结构体??的文章中,我们使用如下的方法来计算一个圆的面积:
但在调用的时候需要使用 ??&
c?
? 来调用它,如何通过方法这一特殊类型的函数来改善呢?
func (c *Circle) area() float64 {
return math.Pi * c.r * c.r
}
Go 没有类。但是,您可以在结构体类型上定义方法。方法定义与函数定义很像。事实上,它们只有一点不同:你需要增加一个额外的参数,一个接收器参数,它在函数名称之前的括号中。
在 ??func?
? 关键字和函数名中间,可以添加一个方法接收器 “receiver”,接收器就像是一个参数(它有名字和类型)。
通过以这种方式创建的方法,它允许我们使用 点号 ??.?
? 运算符,类似这样:
fmt.Println(c.area())
为了调用你定义的方法,键入你要在其上调用方法的值、一个点和要调用的方法的名称,跟着一对括号。这里你调用的方法的值被称为方法接收器。
方法调用和方法定义的相似性能帮助你记住语法:当你调用一个方法时,接收器要被列为第一个,并且当你定义一个方法的时候,接收器参数也被列为第一个。
【Go 语言入门很简单 -- 12. Go 方法 #私藏项目实操分享#】
这样读起来就容易多了,我们不再需要 ??&
?
? 运算符(Go 自动知道为这个方法传递一个指向圆的指针),而且因为这个函数只能用于 ??Circle?
? 类型,我们可以将该函数的名称改为 ??area?
? 。
方法定义中的接收器参数的名称并不重要,但它的类型很重要;你定义的方法与此类型的值都关联。Go使用接收器参数来代替其他语言中的 “self” 或者 “this” 。
问:我见到其他语言使用的方法接收器在方法块中是一个特定的以self或者this命名的值。Go也这么做吗?
练习
答:Go使用接收器参数来代替 self和 this 。两者有着巨大的不同,self 和 this 是隐含的,而你是显式地声明一个接收器参数。除此以外,接收器的用处相同,Go 没有必要保留 self 和 this 关键字!(如果你想要,你可以将接收器参数命名为 this,但是不要这么做,约定是使用接收器参数类型名称的第一个字母。)
定义一个长方体的结构体类型,利用方法来计算长方形的周长和面积:
package main
import "fmt"
type rectangle struct {
width, height int
}
func (r *rectangle) area() int {
return r.width * r.height
}
func (r *rectangle) perimeter() int {
return 2 * r.width * r.height
}
func main() {
r := rectangle{width: 10, height: 6}
fmt.Println("area: ", r.area())
fmt.Println("perimeter: ", r.perimeter())
}
// area:60
// perimeter:120
嵌入类型
继承是OOPs最重要的支柱,但是Go并没有继承(Is-A),也就是说,没有父类和子类的关系。但是可以通过嵌入来实现类似的功能(Has-A)
一个结构体字段通常表示一种从属关系( has-a relationship)。比如圆有半径,矩形有长和宽。
嵌入类型,或者嵌套类型,这是一种可以把已有的类型声明在新的类型里的一种方式,这种功能对代码复用非常重要。
在其他语言中,利用继承可以做同样的事情,但是在 Go 语言中,没有继承的概念,Go 提倡的代码复用的方式是组合,所以这也是嵌入类型的意义所在,组合而不是继承,所以Go才会更灵活。
假如有一个 Person 结构体:
type Person struct {
Name string
}
func (p *Person) Talk() {
fmt.Println("Hello, my name is ", p.Name)
}
同时,我们想要创建一个 ??Student?
? 结构体,可以创建如下:
type Student struct {
Person Person
School string
}
Go 支持通过嵌入类型来支持这种复合关系,也被成为匿名字段:
type Student struct {
Person
Model string
}
我们使用 ??Person?
? 类型而不用给出一个名字。当以这样的方式定义的时候,可以访问类型名:
s := new(Student)
s.Person.Talk()
同时可以在 ??Student?
?? 结构体中直接调用 ??Person?
? 的方法:
s := new(Student)
s.Talk()
这种关系有点像:人能说话,学生是人,因此学生能说话。来看一下整个例子:
package main
import "fmt"
type Person struct {
Name string
}
type Student struct {
Person
School string
}
func (p *Person) Talk() {
fmt.Println("Hello, my name is ", p.Name)
}
func main() {
s := new(Student)
s.Name = "Michael"
s.Person.Talk()
s1 := new(Student)
s1.Name = "Kobe"
s1.School = "Lauer Merion High School"
s1.Talk()
fmt.Println("I come from ", s1.School)
}
// Hello, my name isMichael
// Hello, my name isKobe
// I come fromLauer Merion High School
方法解析过程编译器使用方法解析过程来寻找方法。当一个方法在一个类型上被调用时,它首先尝试在同类型中找到。如果在同一类型中没有找到,它就会查看所有的嵌入类型;如果没有一个嵌入类型有这个方法,它就会尝试在嵌入类型的嵌入类型中寻找。
代码组织结构
既然我们现在学习了变量、常量、结构体、方法和函数,那么一个包中该怎么同时组织这些结构呢?
方法其实可以在包中的任何文件任意位置上定义,但我的建议是按如下所示组织代码:
package models
// list of packages to import
import (
"fmt"
)
// list of constants
const (
ConstExample = "const before vars"
)
// list of variables
var (
ExportedVar= 42
nonExportedVar = "so say we all"
)
// Main type(s) for the file,
// try to keep the lowest amount of structs per file when possible.
type User struct {
FirstName, LastName string
Location*UserLocation
}
type UserLocation struct {
Citystring
Country string
}
// List of functions
func NewUser(firstName, lastName string) *User {
return &
User{FirstName: firstName,
LastName: lastName,
Location: &
UserLocation{
City:"Santa Monica",
Country: "USA",
},
}
}
// List of methods
func (u *User) Greeting() string {
return fmt.Sprintf("Dear %s %s", u.
推荐阅读
- #私藏项目实操分享#Maven实战技巧「插件使用专题」Maven-Archetype插件创建自定义maven项目骨架
- #私藏项目实操分享# SAP 软件的精髓之一(各种各样的决定机制 - Determination Logic)
- 大厂高频面试题Spring Bean生命周期最详解
- #星光计划2.0#基于3861智能开发套件软件开发环境搭建
- 2021-Java后端工程师面试指南-(并发-多线程)#yyds干货盘点#
- 16张图解锁Spring的整体脉络#yyds干货盘点#
- sqoop用法之mysql与hive数据导入导出#yyds干货盘点#
- Linux启动过程
- PHP变量包含带有动态src的iframe()