已经学习了面向对象的概述和结构体的知识(快速入门,Go语言面向对象编程,从0到1保姆级视频教程),本节将开始方法和接口的学习

学习目标

(1)能够掌握方法

(2)能够掌握接口

知识讲解

Ø 方法

方法的概念

Go语言同时有函数和方法,方法的本质是函数,但是方法和函数又具有不同点。

1. 含义不同

函数(function)是一段具有独立功能的代码,可以被反复多次调用,从而实现代码复用。而方法(method)是一个类的行为功能,只有该类的对象才能调用。

2. 方法有接受者,而函数无接受者

Go语言的方法(method)是一种作用于特定类型变量的函数。这种特定类型变量叫做Receiver(接受者、接收者、接收器)。接受者的概念类似于传统面向对象语言中的this或self关键字。

Go语言的接受者强调了方法具有作用对象,而函数没有作用对象。一个方法就是一个包含了接受者的函数。

Go语言中,接受者的类型可以是任何类型,不仅仅是结构体,也可以是struct类型外的其他任何类型。

3. 函数不可以重名,而方法可以重名

只要接受者不同,方法名就可以相同。

Ø 基本语法

方法的语法格式如下。

接受者在func关键字和方法名之间编写,接受者可以是struct类型或非struct类型,可以是指针类型和非指针类型。 接受者中的变量在命名时,官方建议使用接受者类型的第一个小写字母。

下面通过一个示例对比函数和方法在语法上的区别,具体如例所示。

Ø 方法和函数

既然可以用函数来写相同的程序,却还要使用方法,主要有以下两个原因。

l Go不是一种纯粹面向对象的编程语言,它不支持类。因此其方法是一种实现类似于类的行为的方法。

l 相同名称的方法可以在不同的类型上定义,而具有相同名称的函数是不允许的。假设有一个正方形和圆形的结构。可以在正方形和圆形上定义一个名为Area的求取面积的方法。

下面通过一个示例来观察不同的结构体中方法名,如例所示。

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

当指针当接受者时,情况如例所示。

Ø 方法继承

方法是可以继承的,如果匿名字段实现了一个方法,那么包含这个匿名字段的struct也能调用该匿名结构体中的方法。

使用方式如例所示。

Ø 方法重写

方法是可以继承和重写的。

使用方式如例所示。

Ø 接口

接口的概念

面向对象语言中,接口用于定义对象的行为。接口只指定对象应该做什么,实现这种行为的方法(实现细节)是由对象来决定。

在Go语言中,接口是一组方法签名。接口只指定了类型应该具有的方法,类型决定了如何实现这些方法。当某个类型为接口中的所有方法提供了具体的实现细节时,这个类型就被称为实现了该接口。接口定义了一组方法,如果某个对象实现了该接口的所有方法,则此对象就实现了该接口。

Go语言的类型都是隐式实现接口的。任何定义了接口中所有方法的类型都被称为隐式地实现了该接口。

Ø 接口的定义与实现

定义接口的语法格式如下。

实现接口方法的语法格式如下。

具体使用方式如例所示。

Ø duck typing

go没有 implements或extends 关键字,其实这种编程语言叫做duck typing编程语言。

1. 大黄鸭是不是鸭子

生活中的大黄鸭,如图所示。

从生物学角度看,鸭子属于脊索动物门、脊椎动物亚门、鸟纲雁形目。因为大黄鸭没有生命,所以不是鸭子。

2. duck typing

Duck typing是描述事物的外部行为而非内部结构。"当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。"扩展后,可以理解为:“看起来像鸭子,那么它就是鸭子”。在鸭子类型中,关注的不是对象的类型本身,而是它是如何使用的。

3. duck typing编程语言

使用 duck typing 的编程语言往往被归类到“动态类型语言”或者“解释型语言”里,比如 Python, Javascript, Ruby 等等,而非duck typing语言往往被归到“静态类型语言“中,比如 C/C++/Java。

4. 非duck typing语言

以 Java为例, 一个类必须显式地声明:“类实现了某个接口”, 然后才能用在这个接口可以使用的地方。 如果有一个第三方的 Java 库,这个库中的某个类没有声明它实现了某个接口,那么即使这个类中真的有那些接口中的方法,也不能把这个类的对象用在那些要求用接口的地方。但如果在duck typing的语言中就可以这样做,因为它不要求一个类显式地声明它实现了某个接口。

5. 动态类型的优缺点

动态类型的好处很多,Python代码写起来很快。但是缺陷也是显而易见的:错误往往要在运行时才能被发现。相反,静态类型语言往往在编译时就发现这类错误:如果某个变量的类型没有显式的声明实现了某个接口,那么,这个变量就不能用在要求一个实现了这个接口的地方。

Go 类型系统采取了折中的办法,静态类型语言。原因如下。

第一,结构体类型T不需要显式地声明它实现了接口 I。只要类型 T 实现了接口 I 规定的所有方法,它就自动地实现了接口 I。 这样就像动态语言一样省了很多代码,少了许多限制。

第二,将结构体类型的变量显式或者隐式地转换为接口 I 类型的变量 i。这样就可以和其他静态类型语言一样,在编译时检查参数的合法性。

接下来通过一个案例加深对duck typing的理解,详情如例所示。

一个函数如果接收接口类型作为参数,那么实际上可以传入该接口的任意实现类的对象作为参数。定义一个接口变量,那么实际上可以赋值任意实现了该接口的对象。如果定义了一个接口类型的容器(数组或切片),实际上该容器中可以存储任意的实现类对象。

Ø 多态

如果有几个相似而不完全相同的对象,有时人们要求在向它们发出同一个消息时,它们的反应各不相同,分别执行不同的操作。这种情况就是多态现象。例如,甲乙丙三个班都是初中一年级,他们有基本相同的属性和行为,在同时听到上课铃声的时候,他们会分别走向三个不同的教室,而不会走向同一个教室。

多态就是事物的多种形态,Go语言中的多态性是在接口的帮助下实现的。定义接口类型,创建实现该接口的结构体对象。

定义接口类型的对象,可以保存实现该接口的任何类型的值。Go语言接口变量的这个特性实现了Go语言中的多态性。接口类型的对象,不能访问其实现类中的属性字段。

Ø 空接口

空接口中没有任何的方法。任意类型都可以实现该接口。空interface这样定义:interface{},也就是包含0个method的interface。用空接口表示任意数据类型。类似于java中的object。

空接口常用于以下情形:

l println的参数就是空接口

l 定义一个map:key是string,value是任意数据类型

l 定义一个切片,其中存储任意类型的数据

Ø 接口对象转型

接口对象转型第一种方式示例如下。

如果该接口对象是对应的实际类型,那么instance就是转型之后对象,ok的值为true,配合if ... else if...语句使用。

接口对象转型第二种方式示例如下。

配合switch...case语句使用。


知识点总结

https://www.zhihu.com/video/1553380232492691456

Go语言面向对象编程主要讲解了两部分内容,第一部分是面向对象思想以及编写结构体与方法,一个对象其实也就是一个简单的值或者一个变量,在这个对象中会包含一些方法,而一个方法则是一个一个和特殊类型关联的函数。第二部分是通过接口实现多态,接口类型是对其它类型行为的抽象和概括,因为接口类型不会和特定的实现细节绑定在一起,通过这种抽象的方式可以让函数更加灵活和更具有适应能力。简单的用面向对象的思想来描述后厨的场景,对于厨师的职称可以叫做类,雇佣的厨师王师傅叫做对象,身高体重等叫做属性,做菜叫做方法。多态就是,同样是做菜方法,张师傅是洗菜,王师傅是切菜,李师傅是炒菜。值得注意的是,不是任何事物都需要被当成一个对象,独立的函数也有自己的用处。