一.  go方法

go方法:在函数的func和函数名间增加一个特殊的接收器类型,接收器可以是结构体类型或非结构体类型。接收器可以在方法内部访问。创建一个接收器类型为Type的methodName方法。

func (t Type) methodName(parameter list) {
}

go引入方法的原因:

1)go不是纯粹的面向对象编程语言,而且Go不支持类。因此,基于类型的方法是一种实现和类相似行为的途径。

2)相同名字的方法可以定义在不同的类型上,而相同名字的函数不被允许。

方法调用

t.methodName(parameter list)

指针接收器与值接收器

区别:指针接收器的方法内部的改变对外可见,而值接收器不会改变方法外部的变量。

对于指针接收器&T Type而言,(&T).methodName与T.methodName等价。

匿名字段的方法

属于结构体的匿名字段的方法可以被直接调用,就好像这些方法是属于定义了匿名字段的结构体一样。

在方法中使用值接收器 与 在函数中使用值参数

当一个函数有一个值参数,它只能接受一个值参数。

当一个方法有一个值接收器,它可以接受值接收器和指针接收器。

package main

import "fmt"

type rectangle struct {
length int
width int
} func area(r rectangle){
fmt.Printf("Area Function result: %d\n", (r.length * r.width))
} func (r rectangle)area(){
fmt.Printf("Area method result: %d\n", (r.length * r.width))
} func main(){
r := rectangle{
length: ,
width: ,
} area(r)
r.area() p := &r
// area(p) // cannot use p (type *rectangle) as type rectangle in argument to area
p.area() //通过指针调用接收器
}

在方法中使用指针接收器 与 在函数中使用指针参数

函数使用指针参数只接受指针,而使用指针接收器的方法可以使用值接收器和指针接收器。

在非结构体上的方法

为了在一个类型上定义一个方法,方法的接收器类型定义和方法的定义应该在同一个包中。

对于内建类型,如int,应该在文件中创建一个类型别名,然后创建一个以该类型别名为接收器的方法。

二.  go接口

接口是方法(方法签名,method signature)的集合。当一个类型定义了接口中的所有方法,就称它实现了该接口。与OOP类似,接口定义了一个类型应该具有的方法,由该类型决定如何实现这些方法。

type myInterface interface{
method1()
method2()
}

接口调用

//interface definition
type VowelsFinder interface {
FindVowels() []rune
} type MyString string //MyString implements VowelsFinder
func (ms MyString) FindVowels() []rune {
var vowels []rune
for _, rune := range ms {
if rune == 'a' || rune == 'e' || rune == 'i' || rune == 'o' || rune == 'u' {
vowels = append(vowels, rune)
}
}
return vowels
} name := MyString("Sam Anderson")
var v VowelsFinder
v = name // possible since MyString implements VowelsFinder
fmt.Printf("Vowels are %c", v.FindVowels())

如果一个类型包含了接口中声明的所有方法,那么它就隐式地实现了 Go 接口

接口的内部表示

可以把接口看作内部的一个元组 (type, value)。 type 是接口底层的具体类型(Concrete Type),而 value 是具体类型的值。

type Test interface {
Tester()
} type MyFloat float64 func (m MyFloat) Tester() {
fmt.Println(m)
} func describe(t Test) {
fmt.Printf("Interface type %T value %v\n", t, t)
} func main() {
var t Test
f := MyFloat(89.7)
t = f
describe(t)
t.Tester()
}

输出:

Interface type main.myFloat value 89.7
89.7

空接口

没有包含方法的接口称为空接口。空接口表示为 interface{}。由于空接口没有方法,因此所有类型都实现了空接口。

当指定参数为空接口时,可以接收任意类型,那如何获取参数的值呢?  通过类型断言。 v, ok := p.(int),判定参数是否为int并获取参数值。

类型断言

类型断言用于提取接口的底层值(Underlying Value)。

在语法 i.(T) 中,接口 i 的具体类型是 T,该语法用于获得接口的底层值。

v, ok := i.(T)

如果 i 的具体类型是 T,那么 v 赋值为 i 的底层值,而 ok 赋值为 true。

如果 i 的具体类型不是 T,那么 ok 赋值为 false,v 赋值为 T 类型的零值,此时程序不会报错

类型选择(Type Switch)

类型选择用于将接口的具体类型与很多 case 语句所指定的类型进行比较。它与一般的 switch 语句类似。

类型断言的语法是 i.(type),获取接口的类型

还可以将一个类型和接口相比较。如果一个类型实现了接口,那么该类型与其实现的接口就可以互相比较。

type Describer interface {
Describe()
} type Person struct {
name string
age int
} func (p Person) Describe(){
fmt.Printf("%s is %d years old\n", p.name, p.age)
} func findType(i interface{}){
switch v := i.(type){
case Describer:
v.Describe()
default:
fmt.Printf("unknown type\n")
}
} func main(){
findType("wang")
p := Person{
name: "qing",
age: ,
} findType(p)
}
unknown type
qing is years old

在上面程序中,结构体 Person 实现了 Describer 接口。在第 19 行的 case 语句中,v 与接口类型 Describer 进行了比较。p 实现了 Describer,因此满足了该 case 语句,于是当程序运行到第 32 行的 findType(p) 时,程序调用了 Describe() 方法。

实现接口:指针接受者与值接受者

使用值接受者声明的方法,接口既可以用值来调用,也能用指针调用。

对于使用指针接受者的方法,必须用一个指针或者一个可取得地址的值(&method)来调用。但接口中存储的具体值(Concrete Value)并不能取到地址,对于编译器无法自动获取 a 的地址,于是程序报错。

type Describer interface {
Describe()
}
type Person struct {
name string
age int
} func (p Person) Describe() { // 使用值接受者实现
fmt.Printf("%s is %d years old\n", p.name, p.age)
} type Address struct {
state string
country string
} func (a *Address) Describe() { // 使用指针接受者实现
fmt.Printf("State %s Country %s", a.state, a.country)
} func main() {
var d1 Describer
p1 := Person{"Sam", }
d1 = p1
d1.Describe()
p2 := Person{"James", }
d1 = &p2
d1.Describe() var d2 Describer
a := Address{"Washington", "USA"} /* 如果下面一行取消注释会导致编译错误:
cannot use a (type Address) as type Describer
in assignment: Address does not implement
Describer (Describe method has pointer
receiver)
*/
//d2 = a d2 = &a // 这是合法的
// 因为在第 22 行,Address 类型的指针实现了 Describer 接口
d2.Describe()
}

接口的嵌套

type SalaryCalculator interface {
DisplaySalary()
} type LeaveCalculator interface {
CalculateLeavesLeft() int
} type EmployeeOperations interface {
SalaryCalculator
LeaveCalculator
}

接口的零值

接口的零值是 nil。对于值为 nil 的接口,其底层值(Underlying Value)和具体类型(Concrete Type)都为 nil。对于值为 nil 的接口,由于没有底层值和具体类型,当我们试图调用它的方法时,程序会产生 panic 异常。

Go接口最佳实践

1)倾向于使用小的接口定义,很多接口只包含一个方法。    如Reader,Writer,便于类型实现接口,方法太多,类型实现越麻烦。

2)较大的接口定义,可以由多个小接口定义组合而成。  即接口的嵌套。

3)只依赖于必要功能的最小接口。方法或函数的接口参数的范围或方法越小越好,这样便于参数的调用,和方法或函数被其他程序调用。

如func StoreData(reader Reader) error{},能传递Reader就不传递ReadWriter。

参考:Go 系列教程 —— 17. 方法     Golang tutorial series  英文原版

golang方法和接口的更多相关文章

  1. GoLang之方法与接口

    GoLang之方法与接口 Go语言没有沿袭传统面向对象编程中的诸多概念,比如继承.虚函数.构造函数和析构函数.隐藏的this指针等. 方法 Go 语言中同时有函数和方法.方法就是一个包含了接受者的函数 ...

  2. Golang 中的 面向对象: 方法, 类, 方法继承, 接口, 多态的简单描述与实现

    前言: Golang 相似与C语言, 基础语法与C基本一致,除了广受争议的 左花括号 必须与代码同行的问题, 别的基本差不多; 学会了C, 基本上万变不离其宗, 现在的高级语言身上都能看到C的影子; ...

  3. Go 语言中的方法,接口和嵌入类型

    https://studygolang.com/articles/1113 概述 在 Go 语言中,如果一个结构体和一个嵌入字段同时实现了相同的接口会发生什么呢?我们猜一下,可能有两个问题: 编译器会 ...

  4. Go 语言入门(二)方法和接口

    写在前面 在学习 Go 语言之前,我自己是有一定的 Java 和 C++ 基础的,这篇文章主要是基于A tour of Go编写的,主要是希望记录一下自己的学习历程,加深自己的理解 Go 语言入门(二 ...

  5. Go语言_方法和接口

    方法和接口 本节课包含了方法和接口,可以用这种构造来定义对象及其行为. Go 作者组编写,Go-zh 小组翻译. https://tour.go-zh.org/methods/1 方法 Go 没有类. ...

  6. 带你学够浪:Go语言基础系列 - 10分钟学方法和接口

    文章每周持续更新,原创不易,「三连」让更多人看到是对我最大的肯定.可以微信搜索公众号「 后端技术学堂 」第一时间阅读(一般比博客早更新一到两篇) 对于一般的语言使用者来说 ,20% 的语言特性就能够满 ...

  7. C#属性-索引器-里氏替换-多态-虚方法-抽象-接口-泛型-

    1.属性 //属性的2种写法 public class person { private string _name; public string Name { get { return _name; ...

  8. jbuilder的set!方法重构接口

    https://github.com/rails/jbuilder  的set!方法重构接口, 因为grape没法使用 jBuilder 的缓存,所以直接用 Rails 写 API (1)多个图片 i ...

  9. 使用 highchart 绘制柱状图的通用方法与接口

    本文给出使用 highchart 绘制柱状图的通用方法与接口, 只要指定相应的数据结构和配置, 就可以直接拿来使用. 一.  数据结构与基本接口   一般绘制图形, 会涉及到较复杂的数据结构, 比如使 ...

随机推荐

  1. LeetCode 104. Maximum Depth of Binary Tree(二叉树深度)

    Given a binary tree, find its maximum depth. The maximum depth is the number of nodes along the long ...

  2. resources-plugin-2.6.pom.part.lock (没有那个文件或目录)

    由于 自定义 maven 仓库没权限 /home/repository 自定义目录 [root@localhost Service]# cat /etc/group|grep jenkins jenk ...

  3. 【翻译】Flink Table Api & SQL — Hive —— 读写 Hive 表

    本文翻译自官网:Reading & Writing Hive Tables  https://ci.apache.org/projects/flink/flink-docs-release-1 ...

  4. springboot底层原理简述

    1.maven 子父依赖关系,快速整合第三方框架 2.无配置文件 省略了web.xml,spring.xml,springmvc.xml.mybatis.xml. spring3.0以上提供注解,sp ...

  5. PHP表单select中有0选项的处理

    <div class="layui-inline"> <label class="layui-form-label">是否锁定</ ...

  6. LeNet-5 卷积神经网络结构图

    LeNet-5是Yann LeCun在1998年设计的用于手写数字识别的卷积神经网络,当年美国大多数银行就是用它来识别支票上面的手写数字的,它是早期卷积神经网络中最有代表性的实验系统之一.可以说,Le ...

  7. 009 SpringCloud 学习笔记5-----Hystrix保护机制

    1.概述 Hystrix,英文意思是豪猪,全身是刺,看起来就不好惹,是一种保护机制.Hystrix也是Netflix公司的一款组件.主页:https://github.com/Netflix/Hyst ...

  8. redis连接池(JedisPool)资源归还及timeout详解

    转载. https://blog.csdn.net/yaomingyang/article/details/79043019 一.连接池资源类详解都在注释上 package redis.v1.clie ...

  9. gtest入门

    介绍 gtest是谷歌开发的用来做C++单元测试的测试框架 基本概念 使用gtest,你就需要写断言(assertions),用来检查一个表达式是否为true.断言的结果有三个:正确.非致命错误.致命 ...

  10. Python之路【第二十九篇】:django ORM模型层

    ORM简介 MVC或者MVC框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库,这极大的减轻了开发人员的 ...