GO接口

本文介绍了Go语言中的接口,包括鸭子类型的概念、Go语言与其他语言的对比、接口的值类型、空接口、接口组合等。Go语言的接口是面向接口编程的重要组成部分,允许通过实现特定方法来隐式实现接口,提供了灵活且类型安全的编程方式。
摘要由CSDN通过智能技术生成

概要

go语言是面向接口编程的语言,与传统意义上的面向对象有很大的不同,没有传统很复杂的继承、多态。go语言的面向对象只支持封装。那些依靠继承和多态完成的事情依赖于go的接口完成的。go语言的接口要比传统的语言灵活很多。

#实现fly这个方法的就是实现了这个接口
type Bird interface{
	Fly()
}

duck typing概念

鸭子图片
上图一个萌萌的鸭子,那么这个大黄鸭到底是不是一个鸭子?需要从不同的角度看待这个问题:

  • 传统类型系统:脊索动物门,脊椎动物亚门,鸟纲雁形目,鸭科鸭属动物,是由野生绿头鸭和斑嘴鸭驯化而来
  • duck typing 系统:它走起步来像鸭子,并且叫声像鸭子, 那个它一定是一只鸭子,描述事物的外部行为而非内部结构
    从传统类型系统来讲,上图的鸭子就不是鸭子。从duck typing 角度理解,上图长得像鸭子,黄色的、尖嘴巴的、小小的圆眼睛,就是鸭子。同样一个东西,要从使用的角度来看,判断它是不是鸭子。严格说go属于结构化类型,类似于duck typing

其它语言的duck typing

#python中的duck typing
	def  Download(retriever)
		return retriever.get("www.baidu.com")

Download函数是使用者,retriever是实现duck typing的对象。调用Download必须传递一个retriever给函数,retriever对象需要实现get方法

  • 运行的时候才知道retriever 是否具有 get方法(python没有编译)
  • 实现retriever的人,怎么才知道要实现get方法呢?** 通过注释来说明需要实现哪些接口**
#c++中的duck typing
template<class R>
	string download(const R& retriever){
		return retriever.get("www.baidu.com")
	}

c++中的duck typing是通过模板来支持的,R是template,是个class。这段代码和python类似,retriever什么类型都可以, 没有要实现的get的一个基类。什么类型只要实现了get方法,就能作为download函数的参数传递过去使用。

  • 编译的是时候才知道retriever有没有 get方法。没有get方法编译报错
  • 通过注释来说明需要实现哪些接口

java中没有duck typing 只有类似的代码

<R extends Retriever>
	String Download(R r){
	 	return r.get("www.baidu.com")
	}

java也是使用了模板,R extends Retriever 即 R继承了Retriever,实现了Retriever的接口,这样 Download 函数里面就可以 r.get(“www.baidu.com”),java解决了需要通过注释来说明需要实现哪些接口的缺点。现在是传入的参数R对象必须实现Retriever接口,就不会出现编译或运行时才出现没有get方法的错误。

  • 传入的参数必须实现Retriever接口
  • 不是 duck typing 因为它必须实现Retriever接口,比如一个任意类型实现了get方法,但是还是不能使用,必须实现Retriever接口,才能传递给Download作为参数
  • 实现多个接口就做不到了 ,同时需要实现Readable、Appendable 怎么处理?

go语言的duck typing

  • 一个对象可以同时实现两个接口 接口的组装
  • 同时具备python、c++的duck typing灵活性,不管什么类型只要实现了这个接口的所有方法,就是实现了这个接口
  • 具有java的类型检查机制,不希望通过注释来说明需要什么类型

接口的定义:
使用者(download)------> 实现者(retriever)

  • go语言中接口的定义是由使用者来定义的,比如上图的小黄鸭,小孩子就是认为是个小黄鸭,动物研究员就认为不是鸭子
  • 接口的实现是隐式的,不需要说我实现了retriever,只需要实现接口的方法
  • 实现接口的方法

Go语言接口示例:

使用者

package main
import (
	"ccmouse/interface/duck/mock"
	"fmt"
)
type Retriever interface {
	Get(url string) string
}
//使用者决定实现者需要实现哪些接口,使用者规定需要实现哪些方法
func download(r Retriever) string{
	return r.Get("www.bai.com")
}
func main(){
	var r Retriever
	r = mock.Retrievermock{"this is Retriever class"}
	str := download(r)
	fmt.Println(str)
}

实现者

package mock
type Retrievermock struct {
	Contents string
}
func (r Retrievermock) Get(str string) string {
	return r.Contents
}

接口的值类型

接口变量里面到底是什么

在其他语言中,r可能就是引用指针,指向真实的对象。但是go语言类型都是值类型,所有r并不是指针这么简单,接口变量包含类型和值,这个是可以是真实的值或是指针类型和指向值得指针

类型和值

func main(){
	var r Retriever
	r = mock.Retrievermock{"this first retriever"}
	fmt.Printf("类型:%T,值:%v\n",r,r)
	r = retrieverclass.Retrieverclass{"Mozilla/5.0",time.Minute}
	fmt.Printf("类型:%T,值:%v\n",r,r)
	//str := download(r)
	//fmt.Println(str)
}

类型和值
指针类型和指向值得指针

func main(){
	var r Retriever
	r = mock.Retrievermock{"this first retriever"}
	fmt.Printf("类型:%T,值:%v\n",r,r)
	r = &retrieverclass.Retrieverclass{"Mozilla/5.0",time.Minute}
	fmt.Printf("类型:%T,值:%v\n",r,r)
	//str := download(r)
	//fmt.Println(str)
}

指针类型和值指针
在这里插入图片描述

总结:

  • 接口变量自带指针
  • 接口变量同样采用值传递,几乎不需要使用接口的指针
  • 指针接收者实现只能以指针方式方式使用,值接收者都可以

接口变量的类型判断

type swich
switch case + 变量名.(type) 判断变量的类型

// r.(type)是获取变量类型
func main(){
	var r Retriever
	r = mock.Retrievermock{"this first retriever"}
	r = &retrieverclass.Retrieverclass{"Mozilla/5.0",time.Minute}
	switch v := r.(type) {
		case mock.Retrievermock:
			fmt.Printf("r type is mock.Retrievermock,contents:%s\n",v.Contents)
		case *retrieverclass.Retrieverclass:
			fmt.Printf("r type is  *isretrieverclass.Retrieverclass,value:%v\n",v)
	}
}

类型断言 type assert
变量本身,ok := 变量.(指定类型), ok bool 是该类型true 否则 false,将接口变量转换成指定类型

var r Retriever
r = mock.Retrievermock{"this first retriever"}
r = &retrieverclass.Retrieverclass{"Mozilla/5.0",time.Minute}
//type assert
retri := r.(*retrieverclass.Retrieverclass)
fmt.Println(retri.UserAgent)
//或者是下面的写法
if retri1 ok := r.(*retrieverclass.Retrieverclass) ; ok {
	fmt.Println(retri1.Contents)
}

将接口转换为其他接口

实现某个接口的类型同时实现了另外一个接口,此时可以在两个接口间转换。鸟和狗具有不同的特性,鸟可以飞,狗不能飞,但两种动物都可以行走。如果使用结构体同时实现鸟和狗,让它们具备自己特性的 Fly() 和 Walk() 方法就让鸟和狗各自实现了飞行动物接口(Flyer)和行走动物接口(Walker)。将鸟和狗的实例创建后,被保存到 interface{} 类型的 map 中interface{} 类型表示空接口,意思就是这种接口可以保存为任意类型。对保存有鸟或狗的实例interface{} 变量进行断言操作,如果断言对象是断言指定的类型,则返回转换为断言对象类型的接口;如果不是指定的断言类型时,断言的第二个参数将返回 false。
接口类型,ok := 变量.(接口类型)

package main
import "fmt"
// 定义飞行动物接口
type Flyer interface {
    Fly()
}
// 定义行走动物接口
type Walker interface {
    Walk()
}
// 定义鸟类
type bird struct {
}
// 实现飞行动物接口
func (b *bird) Fly() {
    fmt.Println("bird: fly")
}
// 为鸟添加Walk()方法, 实现行走动物接口
func (b *bird) Walk() {
    fmt.Println("bird: walk")
}
// 定义猪
type dog struct {
}
// 为猪添加Walk()方法, 实现行走动物接口
func (d *dog) Walk() {
    fmt.Println("dog: walk")
}
func main() {
// 创建动物的名字到实例的映射
    animals := map[string]interface{}{
        "bird": new(bird),
        "dog":  new(dog),
    }
    // 遍历映射
    for name, obj := range animals {
        // 判断对象是否为飞行动物
        f, isFlyer := obj.(Flyer)
        // 判断对象是否为行走动物
        w, isWalker := obj.(Walker)
        fmt.Printf("name: %s isFlyer: %v isWalker: %v\n", name, isFlyer, isWalker)
        // 如果是飞行动物则调用飞行动物接口
        if isFlyer {
            f.Fly()
        }
        // 如果是行走动物则调用行走动物接口
        if isWalker {
            w.Walk()
        }
    }
}

空接口 interface{}

空接口是指没有定义任何接口方法的接口,没有定义任何接口方法,意味着Go中的任意对象都可以实现空接口(因为没方法需要实现),任意对象都可以保存到空接口实例变量中。

func main() {
    any := make([]interface{}, 5)
    any[0] = "age"
    any[1] = 12
    any[2] = [3]string{1,2,3}
    for _, value := range any {
    	//可以进行类型断言 判断类型
        fmt.Println(value)
    }
}

接口组合

在 Go语言中,不仅结构体与结构体之间可以嵌套,接口与接口间也可以通过嵌套创造出新的接口。一个接口可以包含一个或多个其他的接口,这相当于直接将这些内嵌接口的方法列举在外层接口中一样。只要接口的所有方法被实现,则这个接口中的所有嵌套接口的方法均可以被调用。示例代码

package main
//定义只能Get请求的接口
type Geter interface {
	Get(url string) string
}
//定义只能Post请求的接口
type Poster interface {
	Post(url string,params map[string]interface{}) string
}
//定义一个组合接口
type CurlTool interface {
	Geter
	Poster
}

CurlTool 接口嵌套组合了 Geter、Poster,也就是说CurlTool 同时拥有了 Geter 和 Poster 的特性。
在代码中使用嵌套组合接口,只需要按照接口实现的规则实现 Geter 接口和 Poster 接口即可。而 CurlTool 接口在使用时,编译器会根据接口的实现者确认它们是否同时实现了 Geter 接口和 Poster 接口,详细实现代码如下:

package objects
import "fmt"
//定义Curl结构体
type Curl struct {
}
//Curl结构体实现Get方法 即是Geter接口的实例
func (c *Curl)Get(url string) string {
	return fmt.Sprintf("请求的地址:%s,get方式请求成功!",url)
}
//Curl结构体实现Post方法 即是Poster接口的实例
func (c *Curl) Post(url string,params map[string]interface{}) string {
	return fmt.Sprintf("请求的地址:%s,提交的参数:%v,post方式提交成功!",url,params)
}
//使用者
func DownLoad(tool CurlTool){
	res := tool.Get("http://www.baidu.com")
	fmt.Println(res)
}
//使用者
func Submit(tool CurlTool){
	res := tool.Post("http://www.baidu.com", map[string]interface{}{"username":"jack","age":20,"email":"1811125@163.com"})
	fmt.Println(res)
}
func main(){
	//定义变量ct CurlTool接口
	var ct CurlTool
	//objects.Curl 实现者
	ct = &objects.Curl{}
	Submit(ct)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值