zoukankan      html  css  js  c++  java
  • 12-Go语言之接口(interface)

    内容目录

    • 接口

    内容目录

    接口定义

    • GO语言是面向接口编程
    • 接口是一种抽象的类型
    • 接口(interface)定义了一个对象的行为规范,只定义规范不实现,由具体的对象来实现规范的细节。

    每个接口由数个方法组成,接口的定义格式如下:

    type 接口类型名 interface{
        方法名1( 参数列表1 ) 返回值列表1
        方法名2( 参数列表2 ) 返回值列表2
        …
    }
    

    其中:

    • 接口名:使用type将接口定义为自定义的类型名。Go语言的接口在命名时,一般会在单词后面添加er,如有写操作的接口叫Writer,有字符串功能的接口叫Stringer等。接口名最好要能突出该接口的类型含义。
    • 方法名:当方法名首字母是大写且这个接口类型名首字母也是大写时,这个方法可以被接口所在的包(package)之外的代码访问。
    • 参数列表、返回值列表:参数列表和返回值列表中的参数变量名可以省略。

    实现接口的条件

    • 一个对象只要全部实现了接口中的方法,那么就实现了这个接口。换句话说,接口就是一个需要实现的方法列表。我们来定义一个Sayer接口:
    • 注意:接口内只有方法
    // Sayer 接口
    type Sayer interface {
    	say()
    }
    

    定义dogcat两个结构体:

    type dog struct {}
    
    type cat struct {}
    

    因为Sayer接口里只有一个say方法,所以我们只需要给dogcat 分别实现say方法就可以实现Sayer接口了。

    // dog实现了Sayer接口
    func (d dog) say() {
    	fmt.Println("汪汪汪")
    }
    
    // cat实现了Sayer接口
    func (c cat) say() {
    	fmt.Println("喵喵喵")
    }
    

    接口的实现就是这么简单,只要实现了接口中的所有方法,就实现了这个接口。

    接口使用

    多态特性

    • 使用接口可以实现面向对象中的多态,任何实现了接口中的方法,都可以用接口调用其本身的方法

      // 接口实现一个人的特性
      type Humaner interface {
      	Say()
      }
      // 定义学生结构体
      type Student struct {
      	name  string
      	score int
      }
      // 定义学生方法
      func (s *Student) Say() {
      	fmt.Printf("Student[%s,%d]瞌睡不断
      ", s.name, s.score)
      }
      // 定义老师结构体
      type Teacher struct {
      	name  string
      	group string
      }
      // 定义老师方法
      func (t *Teacher) Say() {
      	fmt.Printf("Teacher[%s,%s]毁人不倦
      ", t.name, t.group)
      }
      // 自定义类型
      type MyStr string
      // 自定义类型方法
      func (str MyStr) Say() {
      	fmt.Printf("MyStr[%s]原类型是string
      ", str)
      }
      // 定义接口方法
      func WhoSay(i Humaner){
      	i.Say()
      }
      
      func main() {
      	s := &Student{"阿杰",88}		// 实例化学生
      	t := &Teacher{"李老师","GO语言二期"}// 实例化老师
      	var tmp MyStr = "毒鸡汤"		// 实例化自定义类型
      
      	// 方式一:接口调用,多态:
      	WhoSay(s)
      	WhoSay(t)
      	WhoSay(tmp)
      
      	// 方式二:接口调用,利用循环去调用方法
      	x := make([]Humaner,3)
      	x[0] ,x[1],x[2] = s,t,tmp
      	for _,value := range x{
      		value.Say()
      	}
      }
      /* 
      方式一结果:
      Student[阿杰,88]瞌睡不断
      Teacher[李老师,GO语言二期]毁人不倦
      MyStr[毒鸡汤]原类型是string
      
      方式二结果:
      Student[阿杰,88]瞌睡不断
      Teacher[李老师,GO语言二期]毁人不倦
      MyStr[毒鸡汤]原类型是string
      */
      

    继承

    • 使用一个接口继承另一个接口,也会继承接口的方法:

      // 接口实现一个人的特性
      type Humaner interface {
      	Say()
      }
      // 用另一个接口继承Humaner接口
      type Personer interface {
      	Humaner
      	Sing(lyrics string)
      }
      // 定义学生结构体
      type Student struct {
      	name  string
      	score int
      }
      // 定义学生方法
      func (s *Student) Say() {
      	fmt.Printf("Student[%s,%d]瞌睡不断
      ", s.name, s.score)
      }
      // 定义学生方法
      func (s *Student) Sing(lyrics string) {
      	fmt.Printf("Student sing[%s]
      ", lyrics)
      }
      
      func main() {
      	s := &Student{"阿杰",88}		// 实例化学生
      	var p Personer		// 定义接口类型的变量
      	p = s				// 赋值操作
      	p.Say()				// 调用继承的方法
      	p.Sing("葫芦娃")	// 调用继承的方法
      }
      /*
      Student[阿杰,88]瞌睡不断
      Student sing[葫芦娃]
      */
      

    接口接收变量

    • 接口可以接收任何实现了该接口内方法的变量。

    • 接口类型变量:可以存储任何实现了该接口所有方法的对象类型。

      // 接口实现一个动物的特性
      type Animal interface {
      	Talk()
      	Eat()
      	Name()	string	// 方法的返回值,直接写
      }
      
      // 定义结构体
      type Dog struct {
      }
      
      func (d Dog)Talk(){
      	fmt.Println("旺旺旺")
      }
      func (d Dog)Eat(){
      	fmt.Println("啃骨头")
      }
      func (d Dog)Name()string{
      	fmt.Println("名字,旺财")
      	return "旺财"
      }
      
      func main() {
      	var a Animal	// a相当于是一个变量
      	fmt.Println(a)	// 此时a为空值,nil
      	var d Dog
      	// 接口类型可以存放Dog结构体,因为Dog实现了接口所有方法
      	a = d		// 类绑定变量
      	a.Talk()
      	a.Eat()
      	a.Name()
      }
      /*
      <nil>
      旺旺旺
      啃骨头
      名字,旺财
      */
      

    同一类型实现多个接口

    • 同一个类型可以实现多个接口

      // 接口实现一个动物的特性
      type Animal interface {
      	Talk()
      	Eat()
      }
      // 第二个接口
      type Animal2 interface {
      	Run()
      }
      
      // 定义结构体
      type Dog struct {
      }
      // 定义方法
      func (d *Dog) Run() {
      	fmt.Println("奔跑吧旺财")
      }
      func (d *Dog) Talk() {
      	fmt.Println("旺旺旺")
      }
      func (d *Dog) Eat() {
      	fmt.Println("啃骨头")
      }
      
      func main() {
      	var a Animal
      	var d Dog
      	a = &d
      	a.Eat()
      
      	var a2 Animal2
      	a2 = &d
      	a.Talk()	// 同一个结构体可以实现多个接口
      	a2.Run()	// 实现第二个接口
      }
      /*
      啃骨头
      奔跑吧旺财
      */
      

    接口嵌套

    • 接口嵌套后,必须实现嵌套接口内的所有方法才可使用

      // 接口实现一个动物的特性
      type Animal interface {
      	Talk()
      	Eat()
      }
      // 第二个接口
      type Animal2 interface {
      	Run()
      }
      // 第三个接口
      type Animal3 interface {
      	Animal		// 嵌套第一个接口
      	Animal2		// 嵌套第二个接口
      }
      

    空接口

    • 空接口是指没有定义任何方法的接口,因此任何类型都实现了空接口。

    • 空接口类型的变量可以存储任意类型的变量。

      func main(){
      	var x interface{}	// 初始化一个空接口
      	x = 100				// 赋值操作
      	fmt.Println(x)		// 打印的是int类型的100
      	x = "沙河"
      	fmt.Println(x)		// 输出为string类型
      	x = false
      	fmt.Println(x)		// 输出bool类型
      	x = struct {
      		name string
      	}{name:"花花"}
      	fmt.Println(x)		// 输出结构体类型
      }
      

    空接口的应用

    空接口作为函数的参数
    • 使用空接口实现可以接收任意类型的函数参数

      // 空接口作为函数参数
      func showType(a interface{}){
      	fmt.Printf("type:%T
      ",a)
      }
      
      func main(){
      	showType(100)
      	showType(99.99)
      	showType(time.Second)
      }
      // type:int
      // type:float64
      // type:time.Duration
      
    空接口作为map的值
    • 使用空接口实现可以保存任意值的字典。

      func main(){
      	// 定义一个值为空接口的map
      	var stuInfo = make(map[string]interface{},100)
      	stuInfo["阿杰"]	= 100
      	stuInfo["阿豪"]	= true
      	stuInfo["阿呆"]	= 99.99
      	stuInfo["阿水"]	= "阿珍爱上了阿强"
      	stuInfo["时间"]	= time.Now()
      	fmt.Println(stuInfo)
      }
      /* 
      map[
      时间:2020-10-19 17:45:51.4885587 +0800 CST m=+0.004011601 
      阿呆:99.99 
      阿杰:100 
      阿水:阿珍爱上了阿强 
      阿豪:true]
      */
      

    值接收者和指针接收者实现接口的区别

    • 值类型实现接口,指针类型可以存进去

    • 但指针类型实现接口,值类型存不进去

      • 示例一:值类型实现接口,指针类型可以存进去
      // 接口实现一个动物的特性
      type Animal interface {
      	Talk()
      	Eat()
      	Name()	string	// 方法的返回值,直接写
      }
      
      // 定义结构体
      type Dog struct {
      }
      
      func (d Dog)Talk(){
      	fmt.Println("旺旺旺")
      }
      func (d Dog)Eat(){
      	fmt.Println("啃骨头")
      }
      func (d Dog)Name()string{
      	fmt.Println("名字,旺财")
      	return "旺财"
      }
      func main() {
      	var a Animal	// a相当于是一个变量
      	var d *Dog = &Dog{}	// 定义Dog为指针类型
      	a = d		// 类绑定变量
      	// 调用时,内部自动转*(&Dag).Eat()
      	a.Talk()
      	a.Eat()
      	a.Name()
      }
      
      • 示例二:指针类型实现接口,值类型存不进去
      // 接口实现一个动物的特性
      type Animal interface {
      	Talk()
      	Eat()
      	Name()	string	// 方法的返回值,直接写
      }
      
      // 定义结构体
      type Dog struct {
      }
      
      func (d *Dog)Talk(){
      	fmt.Println("旺旺旺")
      }
      func (d *Dog)Eat(){
      	fmt.Println("啃骨头")
      }
      func (d *Dog)Name()string{
      	fmt.Println("名字,旺财")
      	return "旺财"
      }
      
      func main() {
      	var a Animal	// a相当于是一个变量
      	var d Dog
      	// 以下代码编译不通过原因:
      	// 若传值类型,不能获取变量地址,d取不到地址,寻址问题不通过
      	a = d		// 类绑定变量
      	a.Talk()
      	a.Eat()
      	a.Name()
      }
      

    类型断言

    • 想要判断空接口中的值这个时候就可以使用类型断言,语法格式为:

    • 该语法返回两个参数,第一个参数是x转化为T类型后的变量,第二个值是一个布尔值,若为true则表示断言成功,为false则表示断言失败。

      x.(T)
      

      其中:

      • x:表示类型为interface{}的变量
      • T:表示断言x可能是的类型。
    • 例一:

      // 示例一:
      func main() {
      	var x interface{}
      	x = "Hello 北京"
      	v, ok := x.(string)			// 进行类型断言
      	if ok {
      		fmt.Println(v)
      	} else {
      		fmt.Println("类型断言失败")
      	}
      }
      
      // 示例二:comma-ok断言
      type Element interface {
      }
      type Person struct {
      	name string
      	age  int
      }
      
      func main() {
      	list := make([]Element, 3)
      	list[0] = 1
      	list[1] = "hello"
      	list[2] = Person{"小强", 18}
      	for index, element := range list {
      		// 类型断言:value,ok := 元素.(Type)
      		if value, ok := element.(int); ok {
      			fmt.Printf("list[%d]是int类型,值是%d
      ", index, value)
      		} else if value, ok := element.(string); ok {
      			fmt.Printf("list[%d]是string类型,值是%v
      ", index, value)
      		} else if value, ok := element.(Person); ok {
      			fmt.Printf("list[%d]是Person类型,值是[%s,%d]
      ", index, value.name, value.age)
      		} else {
      			fmt.Println("不支持的类型")
      		}
      	}
      }
      
    • 例二:可以用switch语句实现多种类型断言

      func justifyType(x interface{}) {
      	switch v := x.(type) {
      	case string:
      		fmt.Printf("x is a string,value is %v
      ", v)
      	case int:
      		fmt.Printf("x is a int,value  is %v
      ", v)
      	case bool:
      		fmt.Printf("x is a bool,value  is %v
      ", v)
      	case *string:
      		fmt.Printf("x is a string poninter,value  is %v
      ", v)
      	default:
      		fmt.Println("unsupport type!")
      	}
      }
      
      func main(){
      	justifyType(100)
      	s := "你好"
      	justifyType(&s)
      }
      /*
      x is a int,value  is 100
      x is a string poninter,value  is 0xc00002e1f0
      */
      
  • 相关阅读:
    disconf 2.6.36 install
    Kubernetes 1.6.1 Kargo
    50个必备的实用jQuery代码段
    jquery封装插件
    java读取txt文件
    file 创建 txt文件
    想要学习web前端的童鞋们 可以看看哦 ! 我认为这几本书超不错
    jquery 杂记
    以文件流的方式 文件上传 下载
    陌生单词记录
  • 原文地址:https://www.cnblogs.com/lynlearnde/p/13841950.html
Copyright © 2011-2022 走看看