热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Golang入门学习之方法(method)练气九层

Golang入门学习之方法(method)--练气九层-写在前面在上一篇文章《Golang入门学习之结构体(struct)》当中,我们学习了Golang当中结构体(struct)

写在前面

在上一篇文章《Golang入门学习之结构体(struct)》当中,我们学习了Golang当中结构体(struct)的知识点,接下来我们将学习Golang当中的方法(method)。

方法的定义

在Golang当中,方法是作用在接收者(receiver)上的一个函数,接收者是某种类型的变量。因此,方法是一种特殊的函数。这里的接收者可以(几乎,接收者类型不能是一个接口类型或指针类型)任何类型,不仅仅是结构体类型,也就意味着,几乎任何类型都可以方法,甚至是函数类型,或者是int、bool等的别名类型。

我们可以这样理解:一个类型(比如说是结构体)加上它的方法就等价于面向对象语言当中的一个类。

方法的定义格式

func (recv receiver_type) methodName(parameter_list) (return_value_list) { ... }

recv 就像是面向对象语言中的 thisself,但是 Golang 中并没有这两个关键字。随个人喜好,你可以使用 thisself 作为 receiver 的名字。

注意点

在 Golang 中,类型的代码和绑定在它上面的方法的代码可以不放置在一起,它们可以存在在不同的源文件,唯一的要求是:它们必须是同一个包的。请看下面这个例子:

我们在src/go_code/method/model/immortal.go 当中定义了一个修仙者类型

package model

// 修仙者
type immortal struct {
    name   string
    age    int
    gender string
}

然后,我们在src/go_code/method/model/immortal_method.go 当中定义immortal 类型的方法

package model

// 工厂函数
func NewImmortal(age int, name, gender string) *immortal {

    return &immortal{
        name:   name,
        age:    age,
        gender: gender,
    }
}

// Getter
func (recv *immortal) GetName() string {
    return recv.name
}
...

再然后,我们再main包当中使用它

package main

import (
    "fmt"
    "go_code/method/model"
)

func main() {
    i := model.NewImmortal(18,"韩立","男")
    name :=i.GetName()
    fmt.Println(name)
}

输出:

韩立

函数与方法的区别

函数和方法都是一段可复用的代码段。他们的区别在于函数是面向过程的,方法是面向对象。从调用上来看,函数通过函数名进行调用,而方法则通过与实例关联的变量进行调用。

// 函数调用
println("Hello World")
// 方法调用
immortal := model.NewImmortal(18,"韩立","男")
immortal.GetName()

再看Golang当中,方法由接收者类型、方法名、形参列表、返回值列表和方法体五部分构成,并且接收者必须有一个显式的名字,这个名字必须在方法中被使用。而且,接收者类型(receiver_type)必须在和方法同样的包中被声明。

Golang中方法的其他特性

在Golang当中,接收者类型关联的方法不写在类型结构里面(面向对象语言Java的方法是在类当中进行定义的)。因此,在Golang当中方法与接收者类型的耦合更加地宽松,也就是说,数据(字段)与其对应的行为是相互独立。

接收者类型可以是一个值而不是类型的指针吗?

接收者类型可以是一个值而不是类型的指针吗?答案是可以的。但是,基于性能方面的考虑,我并不建议大家这样做。因为接收者是作为值传递给对应的方法的,这相当于将实例的值拷贝传递给方法,这并不是一件划算的买卖。请看下面的例子,接收者完全可以是实例的值。

// 修仙者等级
type Level struct {
    level      string
    levelValue int
}

// 获取等级描述
func (recv Level) GetLevel() string{
    return recv.level
}

func main{
    level := model.Level{"练气九层",9200}
    fmt.Println(level.GetLevel())
}

输出:

练气九层

注意:

指针方法和值方法都可以在指针或非指针上被调用。如下面程序所示,类型 Level 在值上有一个方法 GetLevel(),在指针上有一个方法 SetLevel(),但是可以看到两个方法都可以在两种类型的变量上被调用。

package model


// 修仙者等级
type Level struct {
    level      string
    levelValue int
}

func NewLevel(level string, levelValue int) Level {
    return Level{
        level:      level,
        levelValue: levelValue,
    }
}


// 获取等级描述
func (recv Level) Level() string{
    return recv.level
}

func (recv *Level) SetLevel(level string) {
    recv.level = level
}

package main

import (
    "fmt"
    "go_code/method/model"
)

func main() {

    level := model.NewLevel("练气九层",9200)
    levelPointer := & level
    fmt.Println("晋级之前:",level.Level())
    levelPointer.SetLevel("炼气大圆满")
    fmt.Println("晋级之后:",level.Level())
}

输出:

晋级之前: 练气九层
晋级之后: 炼气大圆满

方法和未导出字段

在上面的例子当中,level类型的字段对包外部而言是不可见的(可以理解为面向对象语言当中的private属性)。因此如果在main包当中直接通过选择器进行访问的话,将会报错。这是,我们可以通过面向对象语言一个众所周知的技术来完成:提供 getter 和 setter 方法。在Golang当中,对于 setter 方法使用 Set 前缀,对于 getter 方法只使用成员名。

关于并发访问对象

对象的字段(属性)不应该由 2 个或 2 个以上的不同线程在同一时间去改变。如果在程序发生这种情况,为了安全并发访问,可以使用包 sync中的方法(比如加个互斥锁)。但是这并不是一个推荐的选项(之后我们将会学习通过 goroutines 和 channels 去探索一种新的方式)。请看下面的例子

src/go_code/method/model/level_lock.go

package model

import "sync"

// 修仙者等级
type levelLock struct {
    Lock sync.Mutex
    level      string
    levelValue int
}

func NewLevelLock(level string, levelValue int) *levelLock {
    return &levelLock{
        level:      level,
        levelValue: levelValue,
    }
}

func (recv *levelLock) SetLevel(level string) {

    recv.level  = level

}

src/go_code/struct/main/level_lock.go

package main

import "go_code/method/model"

func main() {
    level := model.NewLevelLock("练气九层",9200)
    // 获取锁
    level.Lock.Lock()
    //修改值
    level.SetLevel("练气圆满")
    // 释放锁
    defer level.Lock.Unlock()
}

内嵌类型的方法和继承

当一个匿名类型被内嵌在结构体中时,匿名类型的可见方法也同样被内嵌,这在效果上等同于外层类型 继承 了这些方法:将父类型放在子类型中来实现亚型。这个机制提供了一种简单的方式来模拟经典面向对象语言中的子类和继承相关的效果。因为一个结构体可以嵌入多个匿名类型,所以实际上我们可以有一个简单版本的多重继承

在model包当中定义一个immortal2 类型,并让其内嵌一个匿名类型level

src/go_code/method/model/anonymous_type.go:

package model



// 修仙者
type immortal2 struct {
    name   string
    age    int
    gender string
    Level
    lingGen
}

func NewImmortal2(age int, name, gender string,levelName string,levelValue int,lingGenNames...string) *immortal2 {
    return &immortal2{
        name:   name,
        age:    age,
        gender: gender,
        Level:  Level{levelName,levelValue},
        lingGen: lingGen{linGenNames: lingGenNames},
    }
}

src/go_code/method/model/level.go:

package model


// 修仙者等级
type Level struct {
    level      string
    levelValue int
}

func NewLevel(level string, levelValue int) Level {
    return Level{
        level:      level,
        levelValue: levelValue,
    }
}


// 获取等级描述
func (recv Level) Level() string{
    return recv.level
}

func (recv *Level) SetLevel(level string) {
    recv.level = level
}

func (recv *Level) LevelName() string{
    return recv.level
}

src/go_code/method/model/lingen.go:

package model

// 修士的灵根
type lingGen struct {
    linGenNames[] string
}

func NewLinggen(name ...string) *lingGen {
    return &lingGen{linGenNames: name}
}

func (recv *lingGen) LingGenNames() []string {
    return recv.linGenNames
}

在main包当中导入并使用

package main

import (
    "fmt"
    "go_code/method/model"
)

func main() {

    im := model.NewImmortal2(18,"韩立","男",
        "练气九层",9200,"木灵根","水灵根","火灵根","土灵根")
    im.SetLevel("练气大圆满")
    fmt.Println("境界:",im.LevelName())
    fmt.Println("灵根:",im.LingGenNames())
}

输出:

境界: 练气大圆满
灵根: [木灵根 水灵根 火灵根 土灵根]

Go 的类型和方法和其他面向对象语言对比

在如 C++、Java、C# 和 Python这样的面向对象语言中,方法在类的上下文中被定义和继承:在一个对象上调用方法时,运行时会检测类以及它的超类中是否有此方法的定义,如果没有会导致异常发生。

在 Golang 中,这样的继承层次是完全没必要的:如果方法在此类型定义了,就可以调用它,和其他类型上是否存在这个方法没有关系。在这个意义上,Golang具有更大的灵活性。

Golang不需要一个显式的类定义,如同 Java和C++等那样,相反地,“类”是通过提供一组作用于一个共同类型的方法集加类型本身来隐式定义的。类型可以是结构体或者任何用户自定义类型。

总结

在Golang中,类=类型+与之关联的方法集。

在 Golang 中,代码复用通过组合和委托实现,多态通过接口的使用来实现:有时这也叫 组件编程(Component Programming)

相比于类继承,Go 的接口(后面将会详细讲解)提供了更强大、却更简单的多态行为。

写在后面

关于Golang中方法的学习就写到这了。本文当中涉及到的例子可以点击此处下载。如果我的学习笔记能够给你带来帮助,还请多多点赞鼓励。文章如有错漏之处还请各位小伙伴帮忙斧正。


推荐阅读
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 关键词:Golang, Cookie, 跟踪位置, net/http/cookiejar, package main, golang.org/x/net/publicsuffix, io/ioutil, log, net/http, net/http/cookiejar ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • 本文介绍了如何在给定的有序字符序列中插入新字符,并保持序列的有序性。通过示例代码演示了插入过程,以及插入后的字符序列。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • 本文介绍了如何使用Express App提供静态文件,同时提到了一些不需要使用的文件,如package.json和/.ssh/known_hosts,并解释了为什么app.get('*')无法捕获所有请求以及为什么app.use(express.static(__dirname))可能会提供不需要的文件。 ... [详细]
author-avatar
LD系瑰精棂_142
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有