浅谈Go语言多态的实现与interface使用


Posted in Golang onJune 16, 2021

一、多态的含义

对于Java或者是C++而言,我们在使用变量的时候,变量的类型是明确的。但是如果我们希望它可以宽松一点,比如说我们用父类指针或引用去调用方法,但是在执行的时候,能够根据子类的类型去执行子类当中的方法。也就是说实现我们用相同的调用方式调出不同结果或者是功能的情况,这种情况就叫做多态。

举个非常经典的例子,比如说猫、狗和人都是哺乳动物。这三个类都有一个say方法,大家都知道猫、狗以及人类的say是不一样的,猫可能是喵喵叫,狗是汪汪叫,人类则是说话。

class Mammal {
    public void say() {
    System.out.println("do nothing")
    }
}


class Cat extends Mammal{
    public void say() {
    System.out.println("meow");
    }
}


class Dog extends Mammal{
    public void say() {
    System.out.println("woof");
    }
}

class Human extends Mammal{
    public void say() {
    System.out.println("speak");
    }
}

这段代码大家应该都不难看懂,这三个类都是Mammal的子类,假设这个时候我们有一系列实例,它们都是Mammal的子类的实例,但是这三种类型都有,我们希望用一个循环来一起全都调用了。虽然我们接收变量的时候是用的Mammal的父类类型去接收的,但是我们调用的时候却会获得各个子类的运行结果。

比如这样:

class Main {
    public static void main(String[] args) {
        List<Mammal> mammals = new ArrayList<>();
        mammals.add(new Human());
        mammals.add(new Dog());
        mammals.add(new Cat());
        
        for (Mammal mammal : mammals) {
            mammal.say();
        }
    }
}

不知道大家有没有get到精髓,我们创建了一个父类的List,将它各个子类的实例放入了其中。然后通过了一个循环用父类对象来接收,并且调用了say方法。我们希望虽然我们用的是父类的引用来调用的方法,但是它可以自动根据子类的类型调用对应不同子类当中的方法。

也就是说我们得到的结果应该是:

speak

woof

meow

这种功能就是多态,说白了我们可以在父类当中定义方法,在子类当中创建不同的实现。但是在调用的时候依然还是用父类的引用去调用,编译器会自动替我们做好内部的映射和转化。

二、抽象类与接口

这样实现当然是可行的,但其实有一个小小的问题,就是Mammal类当中的say方法多余了。因为我们使用的只会是它的子类,并不会用到Mammal这个父类。所以我们没必要实现父类Mammal中的say方法,做一个标记,表示有这么一个方法,子类实现的时候需要实现它就可以了。

这就是抽象类和抽象方法的来源,我们可以把Mammal做成一个抽象类,声明say是一个抽象方法。抽象类是不能直接创建实例的,只能创建子类的实例,并且抽象方法也不用实现,只需要标记好参数和返回就行了。具体的实现都在子类当中进行。说白了抽象方法就是一个标记,告诉编译器凡是继承了这个类的子类必须要实现抽象方法,父类当中的方法不能调用。那抽象类就是含有抽象方法的类。

我们写出Mammal变成抽象类之后的代码:

abstract class Mammal {
    abstract void say();
}

很简单,因为我们只需要定义方法的参数就可以了,不需要实现方法的功能,方法的功能在子类当中实现。由于我们标记了say这个方法是一个抽象方法,凡是继承了Mammal的子类都必须要实现这个方法,否则一定会报错。

抽象类其实是一个擦边球,我们可以在抽象类中定义抽象的方法也就是只声明不实现,也可以在抽象类中实现具体的方法。在抽象类当中非抽象的方法子类的实例是可以直接调用的,和子类调用父类的普通方法一样。但假如我们不需要父类实现方法,我们提出提取出来的父类中的所有方法都是抽象的呢?针对这一种情况,Java当中还有一个概念叫做接口,也就是interface,本质上来说interface就是抽象类,只不过是只有抽象方法的抽象类。

所以刚才的Mammal也可以写成:

interface Mammal {
    void say();
}

把Mammal变成了interface之后,子类的实现没什么太大的差别,只不过将extends关键字换成了implements。另外,子类只能继承一个抽象类,但是可以实现多个接口。早先的Java版本当中,interface只能够定义方法和常量,在Java8以后的版本当中,我们也可以在接口当中实现一些默认方法和静态方法。

接口的好处是很明显的,我们可以用接口的实例来调用所有实现了这个接口的类。也就是说接口和它的实现是一种要宽泛许多的继承关系,大大增加了灵活性。

以上虽然全是Java的内容,但是讲的其实是面向对象的内容,如果没有学过Java的小伙伴可能看起来稍稍有一点点吃力,但总体来说问题不大,没必要细扣当中的语法细节,get到核心精髓就可以了。

讲这么一大段的目的是为了厘清面向对象当中的一些概念,以及接口的使用方法和理念,后面才是本文的重头戏,也就是Go语言当中接口的使用以及理念。

三、Golang中的接口

Golang当中也有接口,但是它的理念和使用方法和Java稍稍有所不同,它们的使用场景以及实现的目的是类似的,本质上都是为了抽象。通过接口提取出了一些方法,所有继承了这个接口的类都必然带有这些方法,那么我们通过接口获取这些类的实例就可以使用了,大大增加了灵活性。

但是Java当中的接口有一个很大的问题就是侵入性,说白了就是会颠倒供需关系。举个简单的例子,假设你写了一个爬虫从各个网页上爬取内容。爬虫爬到的内容的类别是很多的,有图片、有文本还有视频。假设你想要抽象出一个接口来,在这个接口当中定义你规定的一些提取数据的方法。这样不论获取到的数据的格式是什么,你都可以用这个接口来调用。这本身也是接口的使用场景,但问题是处理图片、文本以及视频的组件可能是开源或者是第三方的,并不是你开发的。你定义接口并没有什么卵用,别人的代码可不会继承这个接口。

当然这也是可以解决的, 比如你可以在这些第三方工具库外面自己封装一层,实现你定义的接口。这样当然是OK的,但是显然比较麻烦。

Golang当中的接口解决了这个问题,也就是说它完全拿掉了原本弱化的继承关系,只要接口中定义的方法能对应的上,那么就可以认为这个类实现了这个接口。

我们先来创建一个interface,当然也是通过type关键字:

type Mammal interface {
 Say()
}

我们定义了一个Mammal的接口,当中声明了一个Say函数。也就是说只要是拥有这个函数的结构体就可以用这个接口来接收,我们和刚才一样,定义Cat、Dog和Human三个结构体,分别实现各自的Say方法:

type Dog struct{}

type Cat struct{}

type Human struct{}

func (d Dog) Say() {
 fmt.Println("woof")
}

func (c Cat) Say() {
 fmt.Println("meow")
}

func (h Human) Say() {
 fmt.Println("speak")
}

之后,我们尝试使用这个接口来接收各种结构体的对象,然后调用它们的Say方法:

func main() {
    var m Mammal
    m = Dog{}
    m.Say()
    m = Cat{}
    m.Say()
    m = Human{}
    m.Say()
}

出来的结果当然和我们预想的一样:

浅谈Go语言多态的实现与interface使用

四、总结

今天我们一起聊了面向对象中多态以及接口的概念,借此进一步了解了为什么golang中的接口设计非常出色,因为它解耦了接口和实现类之间的联系,使得进一步增加了我们编码的灵活度,解决了供需关系颠倒的问题。但是世上没有绝对的好坏,golang中的接口在方便了我们编码的同时也带来了一些问题,比如说由于没了接口和实现类的强绑定,其实也一定程度上增加了开发和维护的成本。

总体来说这是一个仁者见仁的改动,有些写惯了Java的同学可能会觉得没有必要,这是过度解绑,有些人之前深受其害的同学可能觉得这个进步非常关键。但不论你怎么看,这都不影响我们学习它,毕竟学习本身是不带立场的。今天的内容当中包含一些Java和面向对象的概念,只是用来引出后面golang的内容,如果存在部分不理解的地方,希望大家抓大放小,理解核心关键就好了,不需要细扣每一个细节。

以上就是浅谈Go语言多态的实现与interface使用的详细内容,更多关于Go 多态与interface的资料请关注三水点靠木其它相关文章!

Golang 相关文章推荐
golang判断key是否在map中的代码
Apr 24 Golang
Go缓冲channel和非缓冲channel的区别说明
Apr 25 Golang
golang fmt格式“占位符”的实例用法详解
Jul 04 Golang
golang实现浏览器导出excel文件功能
Mar 25 Golang
Go 中的空白标识符下划线
Mar 25 Golang
Golang流模式之grpc的四种数据流
Apr 13 Golang
Golang MatrixOne使用介绍和汇编语法
Apr 19 Golang
Golang解析JSON对象
Apr 30 Golang
Golang入门之计时器
May 04 Golang
详解Go语言中配置文件使用与日志配置
Jun 01 Golang
Go Grpc Gateway兼容HTTP协议文档自动生成网关
Jun 16 Golang
再次探讨go实现无限 buffer 的 channel方法
Jun 13 #Golang
Go遍历struct,map,slice的实现
Jun 13 #Golang
go web 预防跨站脚本的实现方式
Jun 11 #Golang
Golang生成Excel文档的方法步骤
Go timer如何调度
浅谈Golang 切片(slice)扩容机制的原理
Jun 09 #Golang
Golang中异常处理机制详解
You might like
Laravel 4 初级教程之安装及入门
2014/10/30 PHP
Yii框架上传图片用法总结
2016/03/28 PHP
用PHP的socket实现客户端到服务端的通信实例详解
2017/02/04 PHP
php制作圆形用户头像的实例_自定义封装类源代码
2017/09/18 PHP
PHP实现的XXTEA加密解密算法示例
2018/08/28 PHP
tp5.1 实现setInc字段自动加1
2019/10/18 PHP
Javascript根据指定下标或对象删除数组元素
2012/12/21 Javascript
jquery中通过父级查找进行定位示例
2013/06/28 Javascript
JQuery 获取json数据$.getJSON方法的实例代码
2013/08/02 Javascript
javascript数组操作总结和属性、方法介绍
2014/04/05 Javascript
有关JS中的0,null,undefined,[],{},'''''''',false之间的关系
2017/02/14 Javascript
原生JS实现自定义滚动条效果
2020/10/27 Javascript
angular中实现li或者某个元素点击变色的两种方法
2017/07/27 Javascript
小程序显示弹窗时禁止下层的内容滚动实现方法
2019/03/20 Javascript
用Vue.js方法创建模板并使用多个模板合成
2019/06/28 Javascript
js实现自动播放匀速轮播图
2020/02/06 Javascript
Python内置的字符串处理函数详细整理(覆盖日常所用)
2014/08/19 Python
以Flask为例讲解Python的框架的使用方法
2015/04/29 Python
python实现将汉字转换成汉语拼音的库
2015/05/05 Python
Python信息抽取之乱码解决办法
2017/06/29 Python
selenium+python 去除启动的黑色cmd窗口方法
2018/05/22 Python
Python使用sax模块解析XML文件示例
2019/04/04 Python
解决在pycharm运行代码,调用CMD窗口的命令运行显示乱码问题
2019/08/23 Python
详解Python 实现 ZeroMQ 的三种基本工作模式
2020/03/24 Python
Python 代码调试技巧示例代码
2020/08/11 Python
pycharm专业版远程登录服务器的详细教程
2020/09/15 Python
Python3爬虫ChromeDriver的安装实例
2021/02/06 Python
详解CSS 3 中的 calc() 方法
2018/01/12 HTML / CSS
PHP面试题及答案一
2012/06/18 面试题
文秘人员工作职责
2014/01/31 职场文书
写求职信要注意什么问题
2014/04/12 职场文书
大学第二课堂活动总结
2014/07/08 职场文书
2015公务员试用期工作总结
2014/12/12 职场文书
2014酒店客房部工作总结
2014/12/16 职场文书
2014年房地产个人工作总结
2014/12/20 职场文书
Python趣味挑战之实现简易版音乐播放器
2021/05/28 Python