gpt4 book ai didi

Go之interface的具体使用

转载 作者:qq735679552 更新时间:2022-09-29 22:32:09 26 4
gpt4 key购买 nike

CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.

这篇CFSDN的博客文章Go之interface的具体使用由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.

浅显地了解了一下 Go,发现 Go 语法的设计非常简洁,易于理解。正应了 Go 语言之父 Rob Pike 说的那句“Less is more”—— 大道至简.

下面就具体的语法特性说说我自己的体会.

interface 。

概览 。

与通常以类型层次与继承为根基的面向对象设计(OOP)语言(如C++、Java)不同,Go 的核心思想就是组合(composition)。Go 进一步解耦了对象与操作,实现了真正的鸭子类型(Duck typing):一个对象如果能嘎嘎叫那就能当做鸭子,而不是像 C++ 或 Java 那样需要类型系统去保证:一个对象先得是只鸭子,然后才能嘎嘎叫.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
type Duck interface {
   Quack()
}
 
type Animal struct {
   name string
}
 
func (animal Animal) Quack() {
   fmt.Println(animal.name, ": Quack! Quack! Like a duck!")
}
 
func main() {
   unknownAnimal := Animal{name: "Unknown"}
 
   var equivalent Duck
   equivalent = unknownAnimal
   equivalent.Quack()
}

运行上面的代码输出:

Unknown : Quack! Quack! Like a duck.

下面用 Java 语言来实现:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
interface Duck {
   void Quack();
}
 
class SomeAnimal implements Duck {
   String name;
 
   public SomeAnimal(String name) {
     this .name = name;
   }
 
   public void Quack() {
     System.out.println(name + ": Quack! Quack! I am a duck!" );
   }
}
 
public class Test {
   public static void main(String []args){
     SomeAnimal unknownAnimal = new SomeAnimal( "Unknown" );
     Duck equivalent = unknownAnimal;
     equivalent.Quack();
   }
}

两相比较就能看出:Go 将对象与对其的操作(方法或函数)解耦得更彻底。Go 并不需要一个对象通过类型系统来保证实现了某个接口(is a),而只需要这个对象实现了某个接口的方法即可(like a),而且类型声明与方法声明或实现也是松耦合的形式。如果稍微转换一下方法的实现方式:

?
1
2
3
func (animal Animal) Quack() {
   fmt.Println(animal.name, ": Quack! Quack! Like a duck!")
}

为:

?
1
2
3
func Quack(animal Animal) {
   fmt.Println(animal.name, ": Quack! Quack! Like a duck!")
}

是不是就和普通方法并无二致了?

在深入浅出 Cocoa 之消息一文中我曾分析过 Objective C 的消息调用过程:

?
1
2
Bird * aBird = [[Bird alloc] init];
[aBird fly];

中对 fly 的调用,编译器通过插入一些代码,将之转换为对方法具体实现 IMP 的调用,这个 IMP 是通过在 Bird 的类结构中的方法链表中查找名称为 fly 的选择子 SEL 对应的具体方法实现找到的,编译器会将消息调用转换为对消息函数 objc_msgSend的调用:

?
1
objc_msgSend(aBird, @selector(fly));

无论是 Objective C 的消息机制还是 Qt 中的 Signal/Slot 机制,可以说都是在尝试将对象本身(数据)与对对象的操作(消息)解耦,但 Go 将这个工作在语言层面做得更加彻底,这样不仅避免多重继承问题,还体现出面向对象设计中最要紧的事情:对象间的消息传递.

实现 。

interface 实际上就是一个结构体,包含两个成员。其中一个成员是指向具体数据的指针,另一个成员中包含了类型信息。空接口和带方法的接口略有不同,下面分别是空接口和带方法的接口是使用的数据结构:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
struct Eface
{
   Type*  type;
   void*  data;
};
struct Iface
{
   Itab*  tab;
   void*  data;
};
 
struct Itab
{
   InterfaceType*  inter;
   Type*  type;
   Itab*  link;
   int32  bad;
   int32  unused;
   void  (*fun[])(void);
};
 
struct Type
{
   uintptr size;
   uint32 hash;
   uint8 _unused;
   uint8 align;
   uint8 fieldAlign;
   uint8 kind;
   Alg *alg;
   void *gc;
   String *string;
   UncommonType *x;
   Type *ptrto;
};

先看Eface,它是interface{}底层使用的数据结构。数据域中包含了一个void*指针,和一个类型结构体的指针。interface{}扮演的角色跟C语言中的void*是差不多的,Go中的任何对象都可以表示为interface{}。不同之处在于,interface{}中有类型信息,于是可以实现反射.

不同类型数据的类型信息结构体并不完全一致,Type是类型信息结构体中公共的部分,其中size描述类型的大小,UncommonType是指向一个函数指针的数组,收集了这个类型的具体实现的所有方法.

在reflect包中有个KindOf函数,返回一个interface{}的Type,其实该函数就是简单的取Eface中的Type域.

Iface和Eface略有不同,它是带方法的interface底层使用的数据结构。data域同样是指向原始数据的,Itab中不仅存储了Type信息,而且还多了一个方法表fun[]。一个Iface中的具体类型中实现的方法会被拷贝到Itab的fun数组中.

Type的UncommonType中有一个方法表,某个具体类型实现的所有方法都会被收集到这张表中。reflect包中的Method和MethodByName方法都是通过查询这张表实现的。表中的每一项是一个Method,其数据结构如下:

?
1
2
3
4
5
6
7
8
9
struct Method
{
   String *name;
   String *pkgPath;
   Type  *mtyp;
   Type *typ;
   void (*ifn)(void);
   void (*tfn)(void);
};

Iface的Itab的InterfaceType中也有一张方法表,这张方法表中是接口所声明的方法。其中每一项是一个IMethod,数据结构如下:

?
1
2
3
4
5
6
struct IMethod
{
   String *name;
   String *pkgPath;
   Type *type;
};

 跟上面的Method结构体对比可以发现,这里是只有声明没有实现的.

Iface中的Itab的func域也是一张方法表,这张表中的每一项就是一个函数指针,也就是只有实现没有声明.

类型转换时的检测就是看Type中的方法表是否包含了InterfaceType的方法表中的所有方法,并把Type方法表中的实现部分拷到Itab的func那张表中.

注意事项 。

一个interface在没有进行初始化时,对应的值是nil。也就是说:

?
1
var v interface{}

此时v就是一个nil。在底层存储上,它是一个空指针.

与之不同的情况 。

?
1
2
3
var obj *T
var v interface{}
v = obj

此时v是一个interface,它的值是nil,也就是说其data域为空,但它自身不为nil.

下面来看个例子就明白了: Go语言中的error类型实际上是抽象了Error()方法的error接口:

?
1
2
3
type error interface {
   Error() string
}

有如下代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
type Error struct {
   errCode uint8
}
 
func (e *Error) Error() string {
   switch e.errCode {
   default:
     return "unknown error"
   }
}
 
func test_checkError() {
   var e *Error
   if e == nil {
     fmt.Println("e is nil")
   } else {
     fmt.Println("e is not nil")
   }
 
   var err error
   err = e
 
   if err == nil {
     fmt.Println("err is nil")
   } else {
     fmt.Println("err is not nil")
   }
}

运行test_checkError()输出:

e is nil err is not nil 。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我.

原文链接:http://blog.csdn.net/kesalin/article/details/79500545 。

最后此篇关于Go之interface的具体使用的文章就讲到这里了,如果你想了解更多关于Go之interface的具体使用的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com