Go语言17-高级-面向对象——接口

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lichangrui2009/article/details/86491591

面向对象——接口

1.接口的概述

  • a.接口是自定义类型,具体描述了一系列方法的集合。
  • b.接口是一种抽象的类型,不会暴露出它所代表的对象的内部值结构和这个对象支持的基础操作的集合。它们只会展示出它们自己的方法,即接口指定了一个类型应该具有的方法,并由该类型决定如何实现这些方法。
  • c.在Go语言中,当一个类型定义了接口中的所有方法。我们称它实现了该接口。

2.接口的定义

type Animaler interface {
    SayHi()
    eat()
}

注:

  • a.接口命名习惯上以er结尾
  • b.接口中只有方法的声明,没有数据字段。

3.接口的实现

  • a.接口是用来定义行为的类型
  • b.被定义的行为不由接口实现,而是由用户定义的类型来实现方法
  • c.在Go语言中,用户定义的类型实现了接口中声明的所有方法,那么这个类型就隐式地实现了接口,而这个类型就是实现这个接口类型的实例

3.1普通接口的实现

普通接口指的是定义了一组方法的接口。普通接口的实现,即用户自定义的类型实现了普通接口的所有方法

3.2 嵌入接口的实现

  • a.接口可以匿名嵌入其他接口,Go语言没有提供继承机制,单可以通过嵌入其他接口,创建一个新接口
  • b.如果一个interface1作为interface2的一个嵌入字段,那么interface2隐式包含了interface1里面的方法。

  • 普通接口的实现
package main

import "fmt"

//定义接口
type Animaler interface {
    SayHi()
    eat()
}

/*定义结构体*/
type Dog struct {
    name   string
    weight int
}

// dog实现SayHi()方法,接收者是普通类型
func (d Dog) SayHi() {
    fmt.Printf("The name of the dog is %s.\n", d.name)
}

// dog实现eat()方法,方法接收者是普通类型
func (d Dog) eat() {
    fmt.Printf("This dog like eat meat!\n")
}

/*定义结构体:猫*/
type Cat struct {
    name  string
    color string
}

//Cat实现SayHi()方法,方法接收者是指针类型
func (c *Cat) SayHi() {
    fmt.Printf("Cat's name is %s,cat's color is %s\n", c.name, c.color)
}

//Cat实现eat()方法,方法接收者是普通类型
func (c Cat) eat() {
    fmt.Printf("This cat like eat fish!\n")
}

/*自动以类型*/
type MyStr string

// MyStr实现SayHi()方法,方法接收者是指针类型
func (str *MyStr) SayHi() {
    fmt.Printf("%s say hi!\n", *str)
}

//MyStr实现eat()方法,方法接收者是指针类型
func (str *MyStr) eat() {
    fmt.Printf("%s like eat meat!\n", *str)
}
func main() {
    dog := Dog{"xiaoqiang", 8}
    cat := Cat{"xiaohua", "white"}
    var tmp MyStr = "wangcai"
    /*两个方法的接收者不论是什么类型调用方法是传入对象可以是结构体类型,
      也可以是结构体指针类型*/
    dog.SayHi()
    (&dog).eat()
    cat.SayHi()
    (&cat).eat()
    (&tmp).SayHi()
    (&tmp).eat()
}

  • 嵌入式接口的实现
package main

import "fmt"

type Cater interface { //接口Cater
    SayHi()
    sing()
}

//接口Doger:包含了接口Cater中的所有方法
type Doger interface {
    Cater
    //接口Cater是Doger接口的嵌入字段
    eat()
}

/*定义结构体:Dog*/
type Dog struct {
    name  string
    color string
}

/*Dog实现SayHi()、sing()、eat()方法,则Dog实现了接口Cater和接口Doger*/
func (d Dog) SayHi() {
    fmt.Printf("The name of the dog is %s.\n", d.name)
}
func (d Dog) sing() {
    fmt.Printf("This dog sing song\n")
}
func (d Dog) eat() {
    fmt.Printf("This dog like eat meat!\n")
}
func main() {
    dog := Dog{"xiaoqiang", "white"}
    dog.SayHi()
    dog.sing()
    dog.eat()
}

4.接口的赋值

4.1将对象实例赋值给接口类型变量

  • a.将某种类型的对象实例赋值给接口变量,要求该对象实现了接口要求的所有方法,这种赋值会把用户定义的类型值存入到这个接口类型变量。
  • b.方法接收者均为值类型,如果将实现该接口所有方法的对象实例赋值给该接口类型的变量,这个对象实例即可以是值类型,也可以是指针类型。
  • c.方法接收者中有一个或几个是指针类型,如果将实现该接口所有方法的对象实例赋值给该接口类型的变量,那么这个对象实例必须是指针类型。因为接口中存储的具体值并不能取地址,程序会报错。
package main

import "fmt"

type Animaler interface { //定义接口: Animaler
    SayHi()
    eat()
}

/*定义结构体Dog*/
type Dog struct {
    name   string
    weight int
}

//Dog实现SayHi()方法,方法接收者是值类型
func (d Dog) SayHi() {
    fmt.Printf("The name of the dog is %s.\n", d.name)
}

func (d Dog) eat() {
    fmt.Printf("This dog like eat meat!\n")
}

/*定义结构体Cat*/
type Cat struct {
    name  string
    color string
}

//Cat实现SayHi()方法,方法接收者是指针类型
func (c *Cat) SayHi() {
    fmt.Printf("Cat's name is %s,cat's color is %s\n", c.name,.color)
}

//Cat实现eat()方法,方法接收者是值类型
func (c Cat) eat() {
    fmt.Printf("This cat like eat fish!\n")
}

//自定义类型MyStr
type MyStr string

//MyStr实现SayHi()方法,方法接收者是指针类型
func (str *MyStr) SayHi() {
    fmt.Printf("%s say hi!\n", *str)
}

// MyStr实现eat()方法,方法接收者是指针类型
func (str *MyStr) eat() {
    fmt.Printf("%s like eat meat!\n", *str)
}

//普通函数,参数为Animaler类型的变量
func WhoSayHiAndEat(i Animaler) {
    i.SayHi()
    i.eat()
}

func main() {
    dog := Dog{"xiaoqiang", 8}
    cat := Cat{"xiaohua", "white"}
    var tmp MyStr = "wangcai"
    fmt.Println("将对象实例给接口变量赋值:")
    /*Dog中的方法接收者均为值类型,赋值给该接口类型变量的实例对象既可以是值类型,可以是指针类型*/
    var k, m, n Animaler
    k = dog
    k.SayHi()
    k.eat()
    k = &dog
    k.SayHi()
    k.eat()
    /*Cat中的方法接收者有一个是指针类型,赋值给该接口变量类型的实例对象只能是指针类型*/
    //m = cat;m.SayHi();m.eat() 编译出错
    m = &cat
    m.SayHi()
    m.eat()
    /*MyStr中额方法接收者均为指针类型接收器,赋值给接口类型的实例对象只能是指针类型*/
    //n = tmp;n.SayHi();n.eat() //编译出错
    n = &tmp
    n.SayHi()
    n.eat()
    /*多太,调用同一接口,不同表现*/
    fmt.Println("同一函数实现调用")
    WhoSayHiAndEat(dog)
    WhoSayHiAndEat(&dog)
    WhoSayHiAndEat(&cat)
    WhoSayHiAndEat(&tmp)
}

4.2.将一个接口赋值给另一个接口

  • a.如果接口A的方法 列表是接口B的方法列表的子集,那么接口B可以赋值给接口A
  • b.接口B被称作超集接口,接口A被称作子集接口。
  • c.超集接口的对象可以转换为子集接口,反之出错。
package main

import "fmt"

/*定义子集接口:Animaler*/
type Animaler interface {
    SayHi()
}

/*定义超集接口Doger:包含子集接口Animaler的所有方法*/
type Doger interface {
    Animaler
    eat()
}

/*定义结构体Dog*/
type Dog struct {
    name  string
    color string
}

//Dog实现SayHi()、eat()方法
func (d Dog) SayHi() {
    fmt.Printf("The name of the cat is %s.\n", d.name)
}
func (d Dog) eat() {
    fmt.Printf("This dog like eat meat!\n")
}
func main() {
    var dog Doger = Dog{"xiaoqiang", "white"}
    //可以将超集接口对象dog转化为子集接口Animal
    var a Animaler = dog
    a.SayHi() //只能实现子集接口中的方法
}

简单点说:子集只有超集的部分接口,所以将超集对象赋值给子集,使用的时候只调用了超集的部分接口。
反过来不行,将子集对象赋值给超集,由于子集只有超集的部分接口,赋值后当做超集对象来的使用,当调用接口时,有可能刚好调用了子集中没有的接口,虽然赋值给了超集,但是实际上它仍是子集,调用它没有的东西,必然出错。

5.空接口

  • a.没有包含方法的接口称为空接口,空接口表示为”interface{}“
  • b.由于空接口没有方法,因此所有类型都实现了空接口,空接口可以存储任意类型的值。
package main

import "fmt"

type Person struct {
    name string
    age  int
}

func printInfo(i interface{}) { //多态
    fmt.Printf("value = %v, type = %T\n", i, i)
}
func main() {
    //将string类型赋值给 interface{}
    var s interface{} = "Hello World"
    printInfo(s)
    var i interface{} = 55
    //将int型赋值给	interface{}
    printInfo(i)
    var j interface{} = &i //将*interface{}类型赋值给interface{}
    printInfo(j)
    //将结构体类型赋值给interface{}
    var p interface{} = Person{"xiaoqiang", 5}
    printInfo(p)
    //将*interface{}类型赋值给interface{}
    var q interface{} = &p
    printInfo(q)
}

6.类型查询

  • a.接口的内部结构可以看做是一对”type:value":type是接口底层的具体类型,而value是具体类型的值
  • b.根据接口变量反向确认接口类型变量保存的类型和值的方法有两种:类型断言和类型选择。

6.1 类型断言

类型断言用于提取接口的底层值。Go语言存在一个语法:根据断言的类型判断这个接口变量存储的是否是该类型的值。

value, ok := i.(T)

注:

  • a.i是接口变量
  • b.T是要断言的类型,value是存储在接口变量里与类型T相对应的值
  • c.ok是布尔值:如果T和value是接口底层的类型和值,ok返回true,否则返回false
package main

import "fmt"

func assert(i interface{}) {
    if v, ok := i.(int); ok {
    fmt.Println("v=", v)
    } else {
    fmt.Println("该接口的底层类型不是int型")
    }
}

func main() {
    var m interface{} = 56
    //整数值存储在空接口型变量
    assert(m)
    var n interface{} = "hello" //字符串的值存储在空接口型变量
    assert(n)
}

6.2 类型选择

  • a.将接口的具体类型与很多case语句所指定的类型进行比较
  • b.与一般的switch语句唯一的区别:类型选择指定的是类型,而一般的switch指定的是值
  • c.关键词switch之后紧跟i.(tyle)
switch i.(type){
case int:
    ...
case string:
    ...
    ...
default:
    ...
}
package main

import "fmt"

func assert(i interface{}) {
    switch i.(type) {
    case int:
        fmt.Printf("i=%v,该接口的底层类型是int \n", i.(int))
    case string:
        fmt.Printf("i=%v,该接口的底层类型是string\n", i.(string))
    default:
        fmt.Printf("该接口的底层类型不知道是什么类型\n")
    }
}

func main() {
    //整数值存储在空接口型变量
    var m interface{} = 56
    assert(m)
    //字符串的值存储在空接口变量
    var n interface{} = "hello"
    assert(n)
    var p interface{} = &n
    assert(p)
}

猜你喜欢

转载自blog.csdn.net/lichangrui2009/article/details/86491591