理解 Golang interface

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

interface 是GO语言的基础特性之一。可以理解为一种类型的规范或者约定。它跟java,C# 不太一样,不需要显示说明实现了某个接口,它没有继承或子类或“implements”关键字,只是通过约定的形式,隐式的实现interface 中的方法即可。因此,Golang 中的 interface 让编码更灵活、易扩展。


一、Golang interface 是什么?

在 Golang 中,interface 是一种抽象的数据类型,相对于抽象类型的是具体类型(int, string 等)。interface 是一组 method 的集合,是鸭子类型(duck-type programming)的一种体现。不关心属性,只关心行为。
具体使用中可以自定义自己的 struct,并提供特定的 interface 里面的 method 就可以把它当做 interface 来使用。下面给出一种 interface 的使用示例。

// 定义接口
type MyInterface interface{
    Print()
}

func TestFunc(x MyInterface) {}
// 定义结构体
type MyStruct struct {}
// 实现接口方法
func (me MyStruct) Print() {}

func main() {
    var me MyStruct
    TestFunc(me)
}

注,鸭子类型:所谓鸭子类型说的是:只要走起路来像鸭子、叫起来也像鸭子,那么就可以把它当做鸭子。

二、为什么有 interface?

1.编写泛型算法

Go官方重磅发布了Go 1.18 beta 1版本,正式支持泛型。

那么在 Go 1.18 beta 1版本之前,可以通过使用 interface 来实现 “泛型编程”,如何实现?因为 interface 是一种抽象类型,任何具体类型(int, string)和抽象类型(user defined)都可以封装成 interface。以标准库的 sort 为例。

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:generate go run genzfunc.go

// Package sort provides primitives for sorting slices and user-defined collections.
package sort

// An implementation of Interface can be sorted by the routines in this package.
// The methods refer to elements of the underlying collection by integer index.
type Interface interface {
	// Len is the number of elements in the collection.
	Len() int

	// Less reports whether the element with index i
	// must sort before the element with index j.
	//
	// If both Less(i, j) and Less(j, i) are false,
	// then the elements at index i and j are considered equal.
	// Sort may place equal elements in any order in the final result,
	// while Stable preserves the original input order of equal elements.
	//
	// Less must describe a transitive ordering:
	//  - if both Less(i, j) and Less(j, k) are true, then Less(i, k) must be true as well.
	//  - if both Less(i, j) and Less(j, k) are false, then Less(i, k) must be false as well.
	//
	// Note that floating-point comparison (the < operator on float32 or float64 values)
	// is not a transitive ordering when not-a-number (NaN) values are involved.
	// See Float64Slice.Less for a correct implementation for floating-point values.
	Less(i, j int) bool

	// Swap swaps the elements with indexes i and j.
	Swap(i, j int)
}

...

// Sort sorts data in ascending order as determined by the Less method.
// It makes one call to data.Len to determine n and O(n*log(n)) calls to
// data.Less and data.Swap. The sort is not guaranteed to be stable.
func Sort(data Interface) {
	n := data.Len()
	quickSort(data, 0, n, maxDepth(n))
}

Sort 函数的形参是一个 interface,包含了 Len()、Less(i, j int)、Swap(i, j int) 三个方法。使用的时候不管数组的元素类型是什么(int, string …),只要实现了这三个方法就可以使用 Sort 函数,这样就实现了 “泛型编程”。
下面给出一个示例(来源)。

package main

import (
	"fmt"
	"sort"
)

type Person struct {
	Name string
	Age  int
}

func (p Person) String() string {
	return fmt.Sprintf("%s: %d", p.Name, p.Age)
}

// ByAge implements sort.Interface for []Person based on
// the Age field.
type ByAge []Person

func (a ByAge) Len() int           { return len(a) }
func (a ByAge) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }

func main() {
	people := []Person{
		{"Bob", 31},
		{"John", 42},
		{"Michael", 17},
		{"Jenny", 26},
	}

	fmt.Println(people)
	// There are two ways to sort a slice. First, one can define
	// a set of methods for the slice type, as with ByAge, and
	// call sort.Sort. In this first example we use that technique.
	sort.Sort(ByAge(people))
	fmt.Println(people)

	// The other way is to use sort.Slice with a custom Less
	// function, which can be provided as a closure. In this
	// case no methods are needed. (And if they exist, they
	// are ignored.) Here we re-sort in reverse order: compare
	// the closure with ByAge.Less.
	sort.Slice(people, func(i, j int) bool {
		return people[i].Age > people[j].Age
	})
	fmt.Println(people)

}

Output:

Output:

[Bob: 31 John: 42 Michael: 17 Jenny: 26]
[Michael: 17 Jenny: 26 Bob: 31 John: 42]
[John: 42 Bob: 31 Jenny: 26 Michael: 17]

2.隐藏具体实现

比如设计一个函数给你返回一个 interface,那么你只能通过 interface 里面的方法来做一些操作,但是内部的具体实现是完全不知道的。关于 context package,参考这篇文章

3.提供拦截点(providing interception points)

参考的文章作者说理解是装饰器,给出了下面的示例,不过没看明白…

type header struct {
    rt  http.RoundTripper
    v   map[string]string
}

func (h header) RoundTrip(r *http.Request) *http.Response {
    for k, v := range h.v {
        r.Header.Set(k,v)
    }
    return h.rt.RoundTrip(r)
}

上面代码中 http.RoundTripper 是 net/http 包里面定义的 interface。通过 interface,我们可以通过类似这种方式实现 dynamic dispatch。

4.多态(polymorphism)

同一种操作会基于多态类型表现出不同的行为。根因是多态类型可以赋值成不同的类型。在 Golang 中,interface 类比于父类,实现了 interface 就相当于子类。举个具体的例子:要实现多态,我们只需要定义一个方法或者函数可以接受不同类型的参数,然后执行各自的操作就可以了。那么代码如下。其中 PolyFunc 可以同时接受 C1 和 C2 不同类型的参数,也就相当于多态了。

type Legendtkl interface {
    Foo
}
type C1 struct{...}
func (c *C1) Foo() {...}

type C2 struct {...}
func (c *C2) Foo() {...}

// polymorphic method
func PolyFunc(x Legendtkl) {...}

三、非入侵式

比如 java 的 interface 实现,需要显式的声明,这种方式属于侵入式。

public class MyWriter implements io.Writer {}

但是在 Golang 中,interface 具有非侵入式的特性。代码示例如下:

type MyIO struct {}

func (io *MyIO) Read(p []byte) (n int, err error) {...}
func (io *MyIO) Write(p []byte) (n int, err error) {...}

// io package
type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

type ReadWriter interface {
    Reader
    Writer
}

四、类型断言

interface 转换为其它类型,一般称之为断言,举例:

func do(v interface{}) {
    n := v.(int)    // might panic
}

这样写的坏处在于:一旦断言失败,程序将会 panic。一种避免 panic 的写法是使用 type assertion。

func do(v interface{}) {
    n, ok := v.(int)
    if !ok {
        // 断言失败处理
    }
}

总结

interface 是 Golang 语言类型系统的基石,学习好 Golang,interface 是必要学习知识,以下再提供一些关于 interface 的学习资料。
1、6、面向对象的编程思维理解interface。
2、真的理解go interface了吗?

参考

1、深入理解 Go Interface

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

k8s-open

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值