博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Go 语言接口详解(一)
阅读量:2288 次
发布时间:2019-05-09

本文共 3808 字,大约阅读时间需要 12 分钟。

这是『就要学习 Go 语言』系列的第 19 篇分享文章

什么是接口

在一些面向对象的编程语言中,例如 Java、PHP 等,接口定义了对象的行为,只指定了对象应该做什么。行为的具体实现取决于对象。

在 Go 语言中,接口是一组方法的集合,但不包含方法的实现、是抽象的,接口中也不能包含变量。当一个类型 T 提供了接口中所有方法的定义时,就说 T 实现了接口。接口指定类型应该有哪些方法,类型决定如何去实现这些方法。

接口声明

接口的声明类似于结构体,使用类型别名且需要关键字 interface,语法如下:

type Name interface {
Method1(param_list) return_type Method2(param_list) return_type ...}

实际定义一个接口:

type Shape interface {
Area() float32}

上面的代码定义了接口类型 Shape,接口中包含了一个不带参数、返回值为 float32 的方法 Area()。任何实现了方法 Area() 的类型 T,我们就说它实现了接口 Shape。

type Shape interface {
Area() float32}func main() {
var s Shape fmt.Println("value of s is", s) fmt.Printf("type of s is %T\n", s)}

value of s is 
type of s is

上面的代码,由于接口是一种类型,所以可以创建 Shape 类型的变量 s,你是不是很疑惑 s 的类型为什么是 nil?让我们来看下一节!

接口类型值

静态类型和动态类型

变量的类型在声明时指定、且不能改变,称为静态类型。接口类型的静态类型就是接口本身。接口没有静态值,它指向的是动态值。接口类型的变量存的是实现接口的类型的值。该值就是接口的动态值,实现接口的类型就是接口的动态类型。

type Iname interface {
Mname()}type St1 struct {
}func (St1) Mname() {
}type St2 struct {
}func (St2) Mname() {
}func main() {
var i Iname = St1{
} fmt.Printf("type is %T\n",i) fmt.Printf("value is %v\n",i) i = St2{
} fmt.Printf("type is %T\n",i) fmt.Printf("value is %v\n",i)}

type is main.St1value is {
}type is main.St2value is {
}

变量 i 的静态类型是 Iname,是不能改变的。动态类型却是不固定的,第一次分配之后,i 的动态类型是 St1,第二次分配之后,i 的动态类型是 St2,动态值都是空结构体。

有时候,接口的动态类型又称为具体类型,当我们访问接口类型的时候,返回的是底层动态值的类型。

nil 接口值

我们来看个例子:

type Iname interface {
Mname()}type St struct {
}func (St) Mname() {
}func main() {
var t *St if t == nil {
fmt.Println("t is nil") } else {
fmt.Println("t is not nil") } var i Iname = t fmt.Printf("%T\n", i) if i == nil {
fmt.Println("i is nil") } else {
fmt.Println("i is not nil") } fmt.Printf("i is nil pointer:%v",i == (*St)(nil))}

t is nil*main.Sti is not nili is nil pointer:true

是不是很惊讶,我们分配给变量 i 的值明明是 nil,然而 i 却不是 nil。 来看下怎么回事!

动态类型在上面已经讲过,动态值是实际分配的值。记住一点:当且仅当动态值和动态类型都为 nil 时,接口类型值才为 nil。上面的代码,给变量 i 赋值之后,i 的动态值是 nil,但是动态类型却是 *St, i 是一个 nill 指针,所以想等条件不成立。

看下 Go 语言规范:

var x interface{
} // x is nil and has static type interface{} var v *T // v has value nil, static type *T x = 42 // x has value 42 and dynamic type int x = v // x has value (*T)(nil) and dynamic type *T

通过这一节学习,相信你已经很清楚为什么上一节的 Shape 类型的变量的 s 输出的类型是 nil,因为 var s Shape 声明时,s 的动态类型是 nil。

实现接口

看示例:

type Shape interface {
Area() float32}type Rect struct {
width float32 height float32}func (r Rect) Area() float32 {
return r.width * r.height}func main() {
var s Shape s = Rect{
5.0, 4.0} r := Rect{
5.0, 4.0} fmt.Printf("type of s is %T\n", s) fmt.Printf("value of s is %v\n", s) fmt.Println("area of rectange s", s.Area()) fmt.Println("s == r is", s == r)}

type of s is main.Rectvalue of s is {
5 4}area of rectange s 20s == r is true

上面的代码,创建了接口 Shape、结构体 Rect 以及方法 Area()。由于 Rect 实现了接口定义的所有方法,虽然只有一个,所以说 Rect 实现了接口 Shape。

在主函数里,创建了接口类型的变量 s ,值为 nil,并用 Rect 类型的结构体初始化,因为 Rect 结构体实现了接口,所以这是有效的。赋值之后,s 的动态类型变成了 Rect,动态值就是结构体的值 {5.0,4.0}。

可以直接使用 . 语法调用 Area() 方法,因为 s 的具体类型是 Rect,而 Rect 实现了 Area() 方法。

空接口

一个不包含任何方法的接口,称之为空接口,形如:interface{}。因为空接口不包含任何方法,所以任何类型都默认实现了空接口。

举个例子,fmt 包中的 Println() 函数,可以接收多种类型的值,比如:int、string、array等。为什么,因为它的形参就是接口类型,可以接收任意类型的值。

func Println(a ...interface{
}) (n int, err error) {
}

我们来看个例子:

type MyString stringtype Rect struct {
width float32 height float32}func explain(i interface{
}) {
fmt.Printf("type of s is %T\n", i) fmt.Printf("value of s is %v\n\n", i)}func main() {
ms := MyString("Seekload") r := Rect{
5.0, 4.0} explain(ms) explain(r)}

type of s is main.MyStringvalue of s is Seekloadtype of s is main.Rectvalue of s is {
5 4}

上面的代码,创建了自定义的字符串类型 MyString 、结构体 Rect 和 explain() 函数。explain() 函数的形参是空接口,所以可以接收任意类型的值。

关于接口使用的第一部分就讲到这,后续会再开文章给大家讲剩余部分,继续关注!

转载地址:http://bbfnb.baihongyu.com/

你可能感兴趣的文章
git常用操作以及快速入门教程
查看>>
MongoDB 3.0 常见集群的搭建(主从复制,副本集,分片....)
查看>>
在notepad++中 “tab转为空格”、“按tab插入空格”、“文档格式转换”
查看>>
Zend Framework 常用组件概述
查看>>
Zend_Db -> Zend_Db_Adapter
查看>>
Zend_Db -> Zend_Db_Select
查看>>
Zend_Db -> Zend_Db_Table
查看>>
PHP漏洞全解(三)-客户端脚本植入
查看>>
Java学习日记 求最值 排序 选择 冒泡 交换
查看>>
IO流输出系统信息
查看>>
File类常见方法
查看>>
File对象功能-文件列表
查看>>
IO流列出目录下所有内容,带层次
查看>>
IO流删除带内容的目录
查看>>
IO流创建java文件列表
查看>>
Properties存取配置文件
查看>>
记录应用程序运行次数
查看>>
打印流PrintStream和PrintWriter
查看>>
IO流 合并流 SequenceInputStream
查看>>
IO流切割文件
查看>>