接口,接口{},转换
Go中的接口指定一个方法集。任何实现此方法集的类型都被称为实施接口。
接口是隐式实现的。没有贯彻关键字,在其他语言中使用。如果类型定义了接口的整个方法集,则它将实现它。这称为结构化类型,相当于编译时鸭打字。
如果它走路像鸭子,像鸭子一样游泳,像鸭子一样嘎嘎叫,那就是鸭子
重要:在Go中,接口抽象了一组方法(动作),而不是数据。
例子
以下示例取自如何在Go中使用界面说明了可以说话的动物的基本界面,以及满足该界面的不同结构:
type Animal interface {
Speak() string
}
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!”
}
您可以构建[]动物并运行Speak()
在其内容上:
func main() {
animals := []Animal{Dog{}, Cat{}, Llama{}, JavaProgrammer{}}
for _, animal := range animals {
fmt.Println(animal.Speak())
}
}
空界面
这Animal
界面虽小又漂亮,但对于初学者来说,还有一些更简单但又有些复杂的概念需要理解:空界面。
interface{}
Go空界面是一个关键概念。每种类型都通过定义实现它。
接口是一种类型,因此您可以定义例如:
type Dog struct {
Age interface{}
}
并且您还可以将空接口类型作为函数参数传递:
func Eat(t interface{}) {
// ...
}
接受interface{}
并不意味着该函数接受任何类型,而是意味着Eat接受一个值interface{}
类型。
在运行时,Go会将传递的实际值转换为interface{}
价值。
如果您在类型为struct的字段中定义字段interface{}
,您可以为其分配任何类型的值。例如:
package main
import “fmt”
type Dog struct {
Age interface{}
}
func main() {
dog := Dog{}
dog.Age = “3”
fmt.Printf("%#v %T\n", dog.Age, dog.Age)
<span style="color:#a6e22e">dog</span>.<span style="color:#a6e22e">Age</span> = <span style="color:#ae81ff">3</span>
<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">"%#v %T\n"</span>, <span style="color:#a6e22e">dog</span>.<span style="color:#a6e22e">Age</span>, <span style="color:#a6e22e">dog</span>.<span style="color:#a6e22e">Age</span>)
<span style="color:#a6e22e">dog</span>.<span style="color:#a6e22e">Age</span> = <span style="color:#e6db74">"not really an age"</span>
<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">"%#v %T"</span>, <span style="color:#a6e22e">dog</span>.<span style="color:#a6e22e">Age</span>, <span style="color:#a6e22e">dog</span>.<span style="color:#a6e22e">Age</span>)
}
印刷
"3" string
3 int
"not really an age" string
当然,这不限于基本类型:您可以在其中存储切片,地图和任何自定义结构。
接口如何在内部表示
在内部,一个接口价值是两个words
,字节组。
一个词指向基础类型的值。一个字指向数据。
转换次数
当我们做animals := []Animal{Dog{}, Cat{}, Llama{}, JavaProgrammer{}}
以前,Go会自动将我们所有的特定动物类型转换为动物类型。现在,该切片的每个元素都是Animal类型,其下层类型指向特定的物种,例如Dog,Cat和Llama。
但是如果一个方法接受[]interface{}
例如,我们不能只是通过animals
到它,因为类型不匹配。我们要明确地转换每个Animal
到interface{}
之前,有一个循环,如我可以将[] T转换为[]接口{}常问问题:
t := []int{1, 2, 3, 4}
s := make([]interface{}, len(t))
for i, v := range t {
s[i] = v
}
确定基础类型interface{}
如果值是类型interface{}
,您可能要确定基础类型。如何?打开电源.(type)
, 喜欢有效执行解释:
开关也可以用来发现接口变量的动态类型。这种类型开关使用括号内带有关键字type的类型断言的语法。如果开关在表达式中声明了变量,则该变量在每个子句中将具有相应的类型。在这种情况下重用名称也是惯用的,实际上是在每种情况下声明一个具有相同名称但类型不同的新变量。
var t interface{}
t = functionOfSomeType()
switch t := t.(type) {
default:
fmt.Printf("unexpected type %T\n", t) // %T prints whatever type t has
case bool:
fmt.Printf("boolean %t\n", t) // t has type bool
case int:
fmt.Printf("integer %d\n", t) // t has type int
case *bool:
fmt.Printf("pointer to boolean %t\n", *t) // t has type *bool
case *int:
fmt.Printf("pointer to integer %d\n", *t) // t has type *int
}
更多教程:
- 使用NGINX反向代理服务Go服务
- 在Go中复制结构
- Go Web服务器的基础
- 在Go中对地图类型进行排序
- 简而言之去指针
- 转到标签说明
- 开始日期和时间格式
- 使用Go进行JSON处理
- 可变参数函数
- 去弦备忘单
- 转到空界面说明
- 使用VS Code和Delve调试Go
- 命名为Go返回参数
- 在Go中生成随机数和字符串
- Go项目的文件系统结构
- Go中的二进制搜索算法
- 在Go中使用命令行标志
- GOPATH解释
- 使用Go构建一个命令行应用程序:lolcat
- 使用Go构建CLI命令:Cowsay
- 在Go中使用壳管
- Go CLI教程:财富克隆
- 使用Go列出文件夹中的文件
- 使用Go从GitHub获取存储库列表
- 去,将一小段字符串附加到文件中
- 去,将字符串转换为字节片
- 使用Go可视化您本地的Git贡献
- Go CPU和内存分析入门
- 解决Go程序中的“不支持索引”错误
- 测量Go程序中的执行时间
- 使用Go构建Web爬网程序以检测重复的标题
- 最佳实践:指针还是价值接收者?
- 最佳实践:您应该使用方法还是函数?
- Go数据结构:集
- 前往地图备忘单
- 在Go中生成泛型类型的实现
- Go数据结构:字典
- Go数据结构:哈希表
- 在“通过通道”中实现事件侦听器
- Go数据结构:堆栈
- Go数据结构:队列
- Go数据结构:二进制搜索树
- Go数据结构:图形
- Go数据结构:链表
- Go数据结构的完整指南
- 比较Go值
- Go是面向对象的吗?
- 在Go中使用SQL数据库
- 在Go中使用环境变量
- 上篇教程:PostgreSQL支持的REST API
- 在Go Web服务器上启用CORS
- 在Docker容器中部署Go应用程序
- 为什么Go是作为PHP开发人员学习的功能强大的语言
- 去,删除io.Reader.ReadString换行符
- 开始,如何观看更改并重建程序
- 去算一下自约会以来的月份
- 在Go中访问HTTP POST参数