GoLang之方法与接口

Go语言没有沿袭传统面向对象编程中的诸多概念,比如继承、虚函数、构造函数和析构函数、隐藏的this指针等。

方法

Go 语言中同时有函数和方法。方法就是一个包含了接受者(receiver)的函数,receiver可以是内置类型或者结构体类型的一个值或者是一个指针。所有给定类型的方法属于该类型的方法集。

如下面的这个例子,定义了一个新类型Integer,它和int一样,只是为它内置的int类型增加了个新方法Less()

type Integer int 

func (a Integer) Less(b Integer) bool {
return a < b
} func main() {
var a Integer = 1 if a.Less(2) {
fmt.Println("less then 2")
}
}

可以看出,Go语言在自定义类型的对象中没有C++/Java那种隐藏的this指针,而是在定义成员方法时显式声明了其所属的对象。

method的语法如下:

func (r ReceiverType) funcName(parameters) (results)

当调用method时,会将receiver作为函数的第一个参数:

funcName(r, parameters);

所以,receiver是值类型还是指针类型要看method的作用。如果要修改对象的值,就需要传递对象的指针。

指针作为Receiver会对实例对象的内容发生操作,而普通类型作为Receiver仅仅是以副本作为操作对象,并不对原实例对象发生操作。

func (a *Ingeger) Add(b Integer) {
*a += b
} func main() {
var a Integer = 1
a.Add(3)
fmt.Println("a =", a) // a = 4
}

如果Add方法不使用指针,则a返回的结果不变,这是因为Go语言函数的参数也是基于值传递。

注意:当方法的接受者是指针时,即使用值类型调用那么方法内部也是对指针的操作。

之前说过,Go语言没有构造函数的概念,通常使用一个全局函数来完成。例如:

func NewRect(x, y, width, height float64) *Rect {
return &Rect{x, y, width, height}
} func main() {
rect1 := NewRect(1,2,10,20)
fmt.Println(rect1.width)
}

匿名组合

Go语言提供了继承,但是采用了组合的语法,我们将其称为匿名组合,例如:

type Base struct {
name string
} func (base *Base) Set(myname string) {
base.name = myname
} func (base *Base) Get() string {
return base.name
} type Derived struct {
Base
age int
} func (derived *Derived) Get() (nm string, ag int) {
return derived.name, derived.age
} func main() {
b := &Derived{} b.Set("sina")
fmt.Println(b.Get())
}

例子中,在Base类型定义了get()和set()两个方法,而Derived类型继承了Base类,并改写了Get()方法,在Derived对象调用Set()方法,会加载基类对应的方法;而调用Get()方法时,加载派生类改写的方法。

组合的类型和被组合的类型包含同名成员时, 会不会有问题呢?可以参考下面的例子:

type Base struct {
name string
age int
} func (base *Base) Set(myname string, myage int) {
base.name = myname
base.age = myage
} type Derived struct {
Base
name string
} func main() {
b := &Derived{} b.Set("sina", 30)
fmt.Println("b.name =",b.name, "\tb.Base.name =", b.Base.name)
fmt.Println("b.age =",b.age, "\tb.Base.age =", b.Base.age)
}

值语义和引用语义

值语义和引用语义的差别在于赋值,比如

b = a
b.Modify()

如果b的修改不会影响a的值,那么此类型属于值类型;如果会影响a的值,那么此类型是引用类型。

Go语言中的大多数类型都基于值语义,包括:

  • 基本类型,如byte、int、bool、float32、string等;
  • 复合类型,如arry、struct、pointer等;

C语言中的数组比较特别,通过函数传递一个数组的时候基于引用语义,但是在结构体定义数组变量的时候基于值语义。而在Go语言中,数组和基本类型没有区别,是很纯粹的值类型,例如:

var a = [3] int{1,2,3}
var b = a
b[1]++
fmt.Println(a, b) // [1 2 3] [1 3 3]

从结果看,b=a赋值语句是数组内容的完整复制,要想表达引用,需要用指针:

var a = [3] int{1,2,3}
var b = &a    // 引用语义
b[1]++
fmt.Println(a, b) // [1 3 3] [1 3 3]

接口

Interface 是一组抽象方法(未具体实现的方法/仅包含方法名参数返回值的方法)的集合,如果实现了 interface 中的所有方法,即该类/对象就实现了该接口。

Interface 的声明格式:

type interfaceName interface {
//方法列表
}

Interface 可以被任意对象实现,一个类型/对象也可以实现多个 interface;
interface的变量可以持有任意实现该interface类型的对象。

如下面的例子:

package main

    import "fmt"

    type Human struct {
name string
age int
phone string
} type Student struct {
Human //匿名字段
school string
loan float32
} type Employee struct {
Human //匿名字段
company string
money float32
} //Human实现SayHi方法
func (h Human) SayHi() {
fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
} //Human实现Sing方法
func (h Human) Sing(lyrics string) {
fmt.Println("La la la la...", lyrics)
} //Employee重载Human的SayHi方法
func (e Employee) SayHi() {
fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
e.company, e.phone)
} // Interface Men被Human,Student和Employee实现
// 因为这三个类型都实现了这两个方法
type Men interface {
SayHi()
Sing(lyrics string)
} func main() {
mike := Student{Human{"Mike", , "222-222-XXX"}, "MIT", 0.00}
paul := Student{Human{"Paul", , "111-222-XXX"}, "Harvard", }
sam := Employee{Human{"Sam", , "444-222-XXX"}, "Golang Inc.", }
tom := Employee{Human{"Tom", , "222-444-XXX"}, "Things Ltd.", } //定义Men类型的变量i
var i Men //i能存储Student
i = mike    
fmt.Println("This is Mike, a Student:")
i.SayHi()
i.Sing("November rain") //i也能存储Employee
i = tom
fmt.Println("This is tom, an Employee:")
i.SayHi()
i.Sing("Born to be wild") //定义了slice Men
fmt.Println("Let's use a slice of Men and see what happens")
x := make([]Men, )
//这三个都是不同类型的元素,但是他们实现了interface同一个接口
x[], x[], x[] = paul, sam, mike for _, value := range x{
value.SayHi()
}
}

空接口

空interface(interface{})不包含任何的method,正因为如此,所有的类型都实现了空interface。空interface对于描述起不到任何的作用(因为它不包含任何的method),但是空interface在我们需要存储任意类型的数值的时候相当有用,因为它可以存储任意类型的数值。它有点类似于C语言的void*类型。

// 定义a为空接口
var a interface{}
var i int =
s := "Hello world"
// a可以存储任意类型的数值
a = i
a = s

interface的变量里面可以存储任意类型的数值(该类型实现了interface),那么我们怎么反向知道这个interface变量里面实际保存了的是哪个类型的对象呢?目前常用的有两种方法:switch测试、Comma-ok断言。

switch测试如下:

type Element interface{}
type List [] Element type Person struct {
name string
age int
} //打印
func (p Person) String() string {
return "(name: " + p.name + " - age: "+strconv.Itoa(p.age)+ " years)"
} func main() {
list := make(List, )
list[] = //an int
list[] = "Hello" //a string
list[] = Person{"Dennis", } for index, element := range list{
switch value := element.(type) {
case int:
fmt.Printf("list[%d] is an int and its value is %d\n", index, value)
case string:
fmt.Printf("list[%d] is a string and its value is %s\n", index, value)
case Person:
fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)
default:
fmt.Println("list[%d] is of a different type", index)
}
}
}

如果使用Comma-ok断言的话:

func main() {
list := make(List, )
list[] = // an int
list[] = "Hello" // a string
list[] = Person{"Dennis", } for index, element := range list {
if value, ok := element.(int); ok {
fmt.Printf("list[%d] is an int and its value is %d\n", index, value)
} else if value, ok := element.(string); ok {
fmt.Printf("list[%d] is a string and its value is %s\n", index, value)
} else if value, ok := element.(Person); ok {
fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)
} else {
fmt.Printf("list[%d] is of a different type\n", index)
}
}
}

嵌入接口

正如struct类型可以包含一个匿名字段,interface也可以嵌套另外一个接口。

如果一个interface1作为interface2的一个嵌入字段,那么interface2隐式的包含了interface1里面的method。

参考文档:

http://se77en.cc/2014/05/05/methods-interfaces-and-embedded-types-in-golang/

http://se77en.cc/2014/05/04/choose-whether-to-use-a-value-or-pointer-receiver-on-methods/

GoLang之方法与接口的更多相关文章

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

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

  2. golang方法和接口

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

  3. golang面向对象和interface接口

    一. golang面向对象介绍 1.golang也支持面向对象编程,但是和传统的面向对象编程有区别,并不是纯粹的面向对象语言.2.golang没有类(class),golang语言的结合体(struc ...

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

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

  5. golang http 服务器的接口梳理

    golang http 服务器的接口梳理 Golang构建HTTP服务(二)--- Handler,ServeMux与中间件 Hanlde和HandleFunc以及Handler, HandlerFu ...

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

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

  7. Go语言_方法和接口

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

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

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

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

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

随机推荐

  1. static 静态导包

    静态导入 ArrayUtils 类的 INDEX_NOT_FOUND 属性和 add 方法 import static org.apache.commons.lang3.ArrayUtils.INDE ...

  2. Linux的vim三种模式及命令

    一般模式:在Linux终端中输入"vim 文件名"就进入了一般模式,但不能输入文字.编辑模式:在一般模式下按i就会进入编辑模式,此时就可以写程式,按Esc可回到一般模式. 命令模式 ...

  3. 解决Eclipse建立Maven项目后无法建立src/main/java资源文件夹的办法

      建立好一个Maven项目后,如果Java Resources资源文件下没有src/main/java文件夹,并且在手动创建这个文件时提示“已存在文件”. 这说明,在这个项目配置中已经有了src/m ...

  4. js base64位和c# Base64位转换

    <script> /* 编码规则 Base64编码的思想是是采用64个基本的ASCII码字符对数据进行重新编码. 它将需要编码的数据拆分成字节数组. 以3个字节为一组.按顺序排列24 位数 ...

  5. HQL多种查询实现

    1.返回int public int countByUsername(String username) { String hql = "select count(*) from BeanCa ...

  6. 【BZOJ-1576】安全路径Travel Dijkstra + 并查集

    1576: [Usaco2009 Jan]安全路经Travel Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 1044  Solved: 363[Sub ...

  7. 【BZOJ-3648】寝室管理 环套树 + 树状数组 + 点分治

    3648: 寝室管理 Time Limit: 40 Sec  Memory Limit: 512 MBSubmit: 239  Solved: 106[Submit][Status][Discuss] ...

  8. 【BZOJ-3343】教主的魔法 分块

    3343: 教主的魔法 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 950  Solved: 414[Submit][Status][Discuss ...

  9. 【bzoj4241】 历史研究

    http://www.lydsy.com/JudgeOnline/problem.php?id=4241 (题目链接) 看到题目就联想到了[bzoj2809] Apio2012—dispatching ...

  10. CruiseControl.NET/CCNET安装包下载

    链接:http://pan.baidu.com/s/1bHgeCI 密码:d7q1