Golang 之 interface接口全面理解
什么是interface
在面向对象编程中,可以这么说:“接口定义了对象的行为”, 那么具体的实现行为就取决于对象了。
在Go中,接口是一组方法签名(声明的是一组方法的集合)。当一个类型为接口中的所有方法提供定义时,它被称为实现该接口。它与oop非常相似。接口指定类型应具有的方法,类型决定如何实现这些方法。
让我们来看看这个例子: Animal 类型是一个接口,我们将定义一个 Animal 作为任何可以说话的东西。这是 Go 类型系统的核心概念:我们根据类型可以执行的操作而不是其所能容纳的数据类型来设计抽象。
type Animal interface {
Speak() string
}
Animal 为任何具有 Speak 方法的类型。Speak 方法没有参数,返回一个字符串。所有定义了该方法的类型我们称它实现了 Animal 接口。Go 中没有 implements 关键字,判断一个类型是否实现了一个接口是完全是自动地。让我们创建几个实现这个接口的类型:type Dog struct {
}
func (d Dog) Speak() string {
return "Woof!"
}
type Cat struct {
}
func (c Cat) Speak() string {
return "Meow!"
}
type Llama struct {
}
func (l Llama) Speak() string {
return "?????"
}
type JavaProgrammer struct {
}
func (j JavaProgrammer) Speak() string {
return "Design patterns!"
}
我们现在有四种不同类型的动物:Dog、Cat、Llama 和 JavaProgrammer。在我们的 main 函数中,我们创建了一个 []Animal{Dog{}, Cat{}, Llama{}, JavaProgrammer{}} ,看看每只动物都说了些什么:
func main() {
animals := []Animal{Dog{}, Cat{}, Llama{}, JavaProgrammer{}}
for _, animal := range animals {
fmt.Println(animal.Speak())
}
}
interface{} 类型
interface{} 类型,空接口,是导致很多混淆的根源。interface{} 类型是没有方法的接口。由于没有 implements 关键字,所以所有类型都至少实现了 0 个方法,所以 所有类型都实现了空接口。这意味着,如果您编写一个函数以 interface{} 值作为参数,那么您可以为该函数提供任何值。例如:func DoSomething(v interface{}) {
// ...
}
DoSomething 函数内部,v 的类型是什么?新手们会认为 v 是任意类型的,但这是错误的。v 不是任意类型,它是 interface{} 类型。对的,没错!当将值传递给DoSomething 函数时,Go 运行时将执行类型转换(如果需要),并将值转换为 interface{} 类型的值。所有值在运行时只有一个类型,而 v 的一个静态类型是 interface{} 。interface{} 的值呢?(具体到上例来说就是 []Animal 中存的是啥?)在我们上面的例子中,当我们初始化变量 animals 时,我们不需要像这样 Animal(Dog{}) 来显示的转型,因为这是自动地。这些元素都是 Animal 类型,但是他们的底层类型却不相同。
为什么这很重要呢?理解接口是如何在内存中表示的,可以使得一些潜在的令人困惑的事情变得非常清楚。比如,像 “我可以将 []T 转换为 []interface{}
吗?” 这种问题就容易回答了。下面是一些烂代码的例子,它们代表了对 interface{} 类型的常见误解:
package main import (
"fmt"
) func PrintAll(vals []interface{}) {
for _, val := range vals {
fmt.Println(val)
}
} func main() {
names := []string{"stanley", "david", "oscar"}
PrintAll(names)
}
运行这段代码你会得到如下错误:cannot use names (type []string) as type []interface {} in argument to PrintAll。如果想使其正常工作,我们必须将 []string 转为 []interface{}:
package main import (
"fmt"
) func PrintAll(vals []interface{}) {
for _, val := range vals {
fmt.Println(val)
}
} func main() {
names := []string{"stanley", "david", "oscar"}
vals := make([]interface{}, len(names))
for i, v := range names {
vals[i] = v
}
PrintAll(vals)
}
很丑陋,但是生活就是这样,没有完美的事情。(事实上,这种情况不会经常发生,因为 []interface{} 并没有像你想象的那样有用)
指针和接口
Cat 的 Speak() 方法改为指针接收器:func (c *Cat) Speak() string {
return "Meow!"
}
运行上述代码,会得到如下错误:
cannot use Cat literal (type Cat) as type Animal in array or slice literal:
Cat does not implement Animal (Speak method has pointer receiver)
该错误的意思是:你尝试将 Cat 转为 Animal ,但是只有 *Cat 类型实现了该接口。你可以通过传入一个指针 (new(Cat) 或者 &Cat{})来修复这个错误。
animals := []Animal{Dog{}, new(Cat), Llama{}, JavaProgrammer{}}
让我们做一些相反的事情:我们传入一个 *Dog 指针,但是不改变 Dog 的 Speak() 方法:
animals := []Animal{new(Dog), new(Cat), Llama{}, JavaProgrammer{}}
这种方式可以正常工作,因为一个指针类型可以通过其相关的值类型来访问值类型的方法,但是反过来不行。即,一个 *Dog 类型的值可以使用定义在 Dog 类型上的 Speak() 方法,而 Cat 类型的值不能访问定义在 *Cat 类型上的方法。
这可能听起来很神秘,但当你记住以下内容时就清楚了:Go 中的所有东西都是按值传递的。每次调用函数时,传入的数据都会被复制。对于具有值接收者的方法,在调用该方法时将复制该值。例如下面的方法:
func (t T)MyMethod(s string) {
// ...
}
是 func(T, string) 类型的方法。方法接收器像其他参数一样通过值传递给函数。
*Cat 的方法不能被 Cat 类型的值调用了。任何一个 Cat 类型的值可能会有很多 *Cat 类型的指针指向它,如果我们尝试通过 Cat 类型的值来调用 *Cat 的方法,根本就不知道对应的是哪个指针。相反,如果 Dog 类型上有一个方法,通过 *Dog 来调用这个方法可以确切的找到该指针对应的 Gog 类型的值,从而调用上面的方法。运行时,Go 会自动帮我们做这些,所以我们不需要像 C语言中那样使用类似如下的语句 d->Speak() 。结语
我希望读完此文后你可以更加得心应手地使用 Go 中的接口,记住下面这些结论:
- 通过考虑数据类型之间的相同功能来创建抽象,而不是相同字段
interface{}的值不是任意类型,而是interface{}类型- 接口包含两个字的大小,类似于
(type, value) - 函数可以接受
interface{}作为参数,但最好不要返回interface{} - 指针类型可以调用其所指向的值的方法,反过来不可以
- 函数中的参数甚至接受者都是通过值传递
- 一个接口的值就是就是接口而已,跟指针没什么关系
- 如果你想在方法中修改指针所指向的值,使用
*操作符
Golang 之 interface接口全面理解的更多相关文章
- C#.NET常见问题(FAQ)-interface接口如何理解
个人把interface理解为一种比较特殊的判断技巧,不是常规的变量类型比如判断字符串,判断数组,而是判断类的实例是否拥有某些属性或者方法(比如有十个女的穿一样的衣服,头上盖住,让新郎去猜哪一个是他的 ...
- golang基础--Interface接口
接口是一个或多个方法签名名的集合,定义方式如下 type Interface_Name interface { method_a() string method_b() int .... } 只要某个 ...
- Golang 入门系列(四)如何理解interface接口
前面讲了很多Go 语言的基础知识,包括go环境的安装,go语言的语法等,感兴趣的朋友,可以先看看之前的文章.https://www.cnblogs.com/zhangweizhong/category ...
- golang面向对象和interface接口
一. golang面向对象介绍 1.golang也支持面向对象编程,但是和传统的面向对象编程有区别,并不是纯粹的面向对象语言.2.golang没有类(class),golang语言的结合体(struc ...
- Golang基础(8):go interface接口
一:接口概要 接口是一种重要的类型,他是一组确定的方法集合. 一个接口变量可以存储任何实现了接口方法的具体值.一个重要的例子就是io.Reader和io.Writer type Reader inte ...
- Golang的Interface是个什么鬼
问题概述 Golang的interface,和别的语言是不同的.它不需要显式的implements,只要某个struct实现了interface里的所有函数,编译器会自动认为它实现了这个interfa ...
- golang的interface剖析
背景: golang的interface是一种satisfied式的.A类只要实现了IA interface定义的方法,A就satisfied了接口IA.更抽象一层,如果某些设计上需要一些更抽象的 ...
- Golang的反射reflect深入理解和示例
编程语言中反射的概念 在计算机科学领域,反射是指一类应用,它们能够自描述和自控制.也就是说,这类应用通过采用某种机制来实现对自己行为的描述(self-representation)和监测(examin ...
- golang 关于 interface 的学习整理
Golang-interface(四 反射) go语言学习-reflect反射理解和简单使用 为什么在Go语言中要慎用interface{} golang将interface{}转换为struct g ...
随机推荐
- linux安装nord,卸载nord源
需要提前准备好:能使用的sock代理. 1.在这里 https://nordvpn.com/zh/download/linux/ 下载初始安装包,这包不是真正的软件,而是会给你添加一个源,大概为了安全 ...
- 深入探访支付宝双11十年路,技术凿穿焦虑与想象极限 | CYZONE特写
小蚂蚁说: 双11十年间,交易规模的指数级增长不断挑战人们的想象力,而对蚂蚁技术团队来说,这不仅是一场消费盛宴,而是无数次濒临压力和焦虑极限的体验,更是技术的练兵场.如今双11对蚂蚁金服而言,已经绝不 ...
- P1340 兽径管理
传送门 思路: 题目要求每次连边都要输出最小生成树的边权和.如果在线直接套用最小生成树模板肯定会超时,考虑离线处理.记录每一插入边的时间,在所有边都插入完成后排序一遍就可以求最小生成树(按照插入时间的 ...
- Django2.1.5使用xadmin打造适合国人的后台管理系统
python做web开发的话,flask.django是比较火的框架了,django是一个比较大的框架,也是一个快速开发利器.但是,django自带的后台admin不太好看,也不太符合我们国人的习惯, ...
- netty-socketio(一)之helloworld,与springboot整合
netty-socketio是一个开源的Socket.io服务器端的一个java的实现, 它基于Netty框架. 1.参考资料 (1)netty-socketio项目github地址: https:/ ...
- openstack详细笔记-nova-glance-swift-cinder-keystone等
一.openstack各大模块 1.openstack总架构图 http://3ms.huawei.com/km/blogs/details/2655265 2.nova 2.0架构图 2.1 nov ...
- js 获取getElementsTagName()方法返回值的内容
<div id="news-top" class="section"> <h3>Some title</h3> <di ...
- EBS 请求输出Html报表集成Echarts
百度开源的ECharts有样式丰富且美观的报表类型可供选用,可以将其集成至EBS请求输出的Html报表中,这其实就是一个生成Html数据的过程. 定义输出类型为HTML的请求我就不在此处赘述. 我 ...
- css样式 + 特殊符号
color控制字体颜色 十六进制值 #cc0066: font-size控制字体大小 单位 px / % / em / rem:像素 / 相对于父级元素 / 取决自己使用字体大小 / 取决于根元素ht ...
- 下载EU台网(欧洲台网)的地震波数据
retrievedata.py ### here first to check the existence of the focal mechanism event file in the NDK d ...