go-接口

Go语言里面设计最精妙的应该算interface,它让面对对象,内容组织实现的非常方便,接下来将简要记录一下interface的使用:

什么是interface?简单的说,interface是一组method签名的组合,我们通过interface定义对象的一组行为

接口定义

我们可以这样定义一个接口:

type Listener interface{
    print_info()
}

接口有这样几个特点:

  • 接口命名习惯以er结尾
  • 接口只有方法声明,没有实现,没有数据字段
  • 接口可以匿名嵌入到其他接口,或嵌入到结构中

下面,我们来使用一下接口:

package main
import "fmt"

type Listener interface{
    print_info()
}


type Student struct{
    name string
    age int
}
func (s Student) print_info() {
    fmt.Println("Student的print_info")
}


type People struct{
    name string
    age int
}
func (p *People) print_info(){
    fmt.Println("People的print_info")
}


type MyStr string
func (s *MyStr) print_info() {
    fmt.Println("MyStr的print_info")
}


func main(){
    var l Listener
    l = Student{"laozhang", 18}
    l.print_info()
    
    l = &People{"laowang", 20}
    l.print_info()
    
    var s MyStr = "laohei"
    l = &s
    l.print_info()
}

// 打印结果如下
Student的print_info
People的print_info
MyStr的print_info

值得注意的是,上面这个栗子,我们定义了一个接口类型,并且分别在StudentPeople以及MyStr中实现该接口的方法,如果与此接口方法绑定的是一个指定变量,那么在使用此接口方法时,传递给接口变量也应该是指针。这一点与普通的方法有所不同,并不会自动进行转换!

上面栗子中,在接口里定义的是一个无返回值的方法;下面,我们来定义一个有返回值的情况,如下:

package main
import (
    "fmt"
    "strconv"
)

type Listener interface{
    get_info() string
}

type Student struct{
    name string
    age int
}

func (s Student) get_info() string {
    return "name: " + s.name + " age: " + strconv.Itoa(s.age) 
}

func main() {
    s := Student{"laozhang", 18}
    var l Listener = s
    info := l.get_info()
    
    fmt.Println(info)
}

// 打印结果如下
name: laozhang age: 18

多态

多态,简单来说,就是调用同一个方法却有着不同的表现。下面对上面栗子进行修改一下实现一个多态行为:

package main
import "fmt"

type Listener interface{
    print_info()
}


type Student struct{
    name string
    age int
}
func (s Student) print_info() {
    fmt.Println("Student的print_info")
}


type People struct{
    name string
    age int
}
func (p *People) print_info(){
    fmt.Println("People的print_info")
}


type MyStr string
func (s *MyStr) print_info() {
    fmt.Println("MyStr的print_info")
}


func who_print_info(listener Listener){
    listener.print_info()
}

func main(){
    list := make([]Listener, 3)
    list[0] = Student{"laozhang", 18}
    list[1] = &People{"laowang", 20}
    
    var s MyStr = "laohei"
    list[2] = &s
    
    for _, data := range list{
        who_print_info(data)
    }
}

// 打印结果如下
Student的print_info
People的print_info
MyStr的print_info

继承

interface跟struct一样,都拥有继承的功能。我们可以定义两个interface,使其中一个interface以匿名字段的形式存在于另一个interface当中,以达到继承的效果,如下:

package main
import "fmt"

type Listener interface{
    print_info()
}

type ChildListener interface{
    Listener
    print_child()
}

type Student struct{
    name string
    age int
}

func (s Student) print_info() {
    fmt.Println("Student的print_info")
}

func (s Student) print_child() {
    fmt.Println("Student的print_child")
}

func main(){
    var cl ChildListener = Student{"laozhang", 18}
    cl.print_info()
    cl.print_child()
}

// 打印结果如下
Student的print_info
Student的print_child

转换

转换,我们只需要明白一个概念,超集可以转换为子集,而子集不可以转换为超集合。有两个interface,其中一个interface中拥有另一个interface的匿名字段时,我们称这个为超级,那么另一个则为子集。举个栗子:

type Listener interface{
    print_info()
}

type MyListener interface{
    Listener
    print_other()
}

此时,MyListener就是超级,Listener就为子集。我们可以这样进行转换:

var l Listener
var ml MyListener

l = ml

但是,如果子集转换超集,那么就会发生编译错误,下面这种转换方式是错误的:

var l Listener
var ml MyListener

ml = l

空接口

空接口(interface{})不包含任何的方法,正因为如此,所有的类型都实现了空接口,因此空接口可以存储任意类型的数值。它有点类似于C语言的Void *类型,例如:

var v1 interface{} = 1
var v2 interface{} = 'A'
var v3 interface{} = "abc"
var v4 interface{} = 3.12
var v5 interface{} = struct{ x int }{1}
var v6 interface{} = &struct{ x int }{1}

当函数可以接收任何类型参数时,我们可以将其定义为interface{},最典型的就是标准库fmt中的各种打印函数接收的参数,如下:

fmt.Print(a ...interface{})
fmt.Println(a ...interface{})
fmt.Printf(format string, a ...interface{})

我们知道空接口可以存储任意类型的数据,那么我们如何知道这个变量里面实际存储的是什么类型的对象呢?目前常用的方法有两种:

  • Comma-ok

Go语言里有一种语法,可以直接判断该变量是否为该类型的变量:value, ok = element.(T),这里value就是变量的值,ok是一个bool值,element就是我们的空接口类型,T为我们需要判断是否为T类型。

如果element确实存储了T类型的值,那么ok会返回true,否则返回false。举个栗子:

var v interface{} = "abc"
value, ok := v.(int)
value2, ok2 := v.(string)

fmt.Println(value, ok)
fmt.Println(value2, ok2)

// 打印结果如下
0 false
abc true

如果element未存储了T类型的值,那么value会返回T类型的零值!

  • switch-case
var v interface{} = "abc"
switch value := v.(type) {
case int:
    fmt.Printf("int类型, value = %v", value)
case string:
    fmt.Printf("string类型, value = %v", value)
default:
    fmt.Printf("其他类型, value = %v", value)
}

// 打印结果如下
string类型, value = abc

值得注意的是,element.(type)不能在switch以外的任何逻辑里面使用

至此,Over~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值