Go语言基础语法5 | 青训营

75 阅读4分钟

Go语言接口

定义接口

接口又称为动态数据类型,在进行接口使用的的时候,会将接口对位置的动态类型改为所指向的类型,会将动态值改成所指向类型的结构体

//**接口名**为Phone,接口内有speak与read方法即方法名
type Phone interface {
        speak()
        read()
}

接口名:使用type将接口定义为自定义的类型名。Go语言的接口在命名时,一般会在单词后面添加er,如有写操作的接口叫Writer,有字符串功能的接口叫Stringer等。接口名最好要能突出该接口的类型含义。

方法名:当方法名首字母是大写且这个接口类型名首字母也是大写时,这个方法可以被接口所在的包(package)之外的代码访问。

Go 语言提供了另外一种数据类型即接口,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。

接口可以让我们将不同的类型绑定到一组公共的方法上,从而实现多态和灵活的设计。

Go 语言中的接口是隐式实现的,也就是说,如果一个类型实现了一个接口定义的所有方法,那么它就自动地实现了该接口。因此,我们可以通过将接口作为参数来实现对不同类型的调用,从而实现多态。

/* 定义接口 */
type interface_name interface {
   method_name1 [return_type]
   method_name2 [return_type]
   method_name3 [return_type]
   ...
   method_namen [return_type]
}

/* 定义结构体 */
type struct_name struct {
   /* variables */
}

/* 实现接口方法 */
func (struct_name_variable struct_name) method_name1() [return_type] {
   /* 方法实现 */
}
...
func (struct_name_variable struct_name) method_namen() [return_type] {
   /* 方法实现*/
}

接口类型变量

接口类型变量能够存储所有实现了该接口的实例。 例如上面的示例中,Sayer类型的变量能够存储dog和cat类型的变量

func main() {
        var x Sayer // 声明一个Sayer类型的变量x
        a := cat{}  // 实例化一个cat
        b := dog{}  // 实例化一个dog
        x = a       // 可以把cat实例直接赋值给x
        x.say()     // 喵喵喵
        x = b       // 可以把dog实例直接赋值给x
        x.say()     // 汪汪汪
}

实现接口两种方式的区别

值接受者

使用值接收者,可以将值、指针传进去,但是没有办法修改接受者本身的属性,类似于交换两个值时的值传递

//定义接口
type Phone interface {
        speak()
        read()
}
//定义结构体类型,作为接受者
type IPhone struct {
        name string
}
//对特定的结构体进行接口的实现
func (a IPhone) speak() {
        fmt.Println("我叫sir,您好!")
}
指针接收者

使用指针接收者,必须传地址,可以修改指向对象的属性

//定义接口
type Phone interface {
        speak()
        read()
}
//定义结构体类型,作为接受者
type IPhone struct {
        name string
}
//对特定的结构体进行接口的实现
func (a *IPhone) speak() {
        fmt.Println("我叫sir,您好!")
}

类型与接口的关系

一个类型实现多个接口

一个类型可以同时实现多个接口,而接口间彼此独立,不知道对方的实现。 例如,狗可以叫,也可以动。我们就分别定义Sayer接口和Mover接口,如下: Mover接口

// Sayer 接口
type Sayer interface {
        say()
}

// Mover 接口
type Mover interface {
        move()
}
多个类型实现同一接口

Go语言中不同的类型还可以实现同一接口 首先我们定义一个Mover接口,它要求必须由一个move方法。

// Mover 接口
type Mover interface {
        move()
}

空接口

没有任何方法的接口就是空接口,实际上每个类型都实现了空接口,所以空接口类型可以接受任何类型的数据

//定义一个空接口
type phone interface{}
//空接口作为参数,传进来任意类型参数判断其类型与打印其值
func showmpType(q interface{}) {
        fmt.Printf("type:%T,value:%v\n", q, q)
}

类型断言

根据变量不同的类型进行不同的操作

方式一:

有两个返回值,如果断言成功第一个返回值为该变量对应的数据,否则返回该类型的空值,第二个参数是一个布尔值,如果断言成功则返回的是一个true,否则返回false

func judgeType1(q interface{}) {
        temp, ok := q.(string)
        if ok {
                fmt.Println("类型转换成功!", temp)
        } else {
                fmt.Println("类型转换失败!", temp)
        }

}
方式二:

使用switch...case...语句,如果断言成功则到指定分支

func judgeType2(q interface{}) {
        switch i := q.(type) {
        case string:
                fmt.Println("这是一个字符串!", i)
        case int:
                fmt.Println("这是一个整数!", i)
        case bool:
                fmt.Println("这是一个布尔类型!", i)
        default:
                fmt.Println("未知类型", i)
        }
}

接口使用较为灵活,可以在实现的接口内进行本类型对象的操作,在接口外部进行接口方法调用,实现相同的代码段有不同的效果,多态的思想也尤为重要