Go语言教程之边写边学:接口 interface

声明接口类型

接口是一种抽象类型。

接口描述方法集的所有方法,并为每个方法提供签名。

若要创建接口,请使用interface关键字,后跟包含方法名称列表的大括号,以及方法应具有的任何参数或返回值。

// Declare an Interface Type and methods does not have a body
type Employee interface {
	PrintName() string                // Method with string return type
	PrintAddress(id int)              // Method with int parameter
	PrintSalary(b int, t int) float64 // Method with parameters and return type
}

接口充当方法集的蓝图,它们必须在使用之前实现。满足接口的类型即实现该接口。

 

定义满足接口的类型

使用两种方法定义名为Employee的接口类型。然后,它定义一个名为Emp的类型,该类型满足员工

我们在Emp上定义了满足员工所需的所有方法

package main

import "fmt"

// 声明接口
type Employee interface {
	PrintName(name string)
	PrintSalary(basic int, tax int) int
}

// Emp用户自定义类型
type Emp int

// 实现接口方法
func (e Emp) PrintName(name string) {
	fmt.Println("Employee Id:\t", e)
	fmt.Println("Employee Name:\t", name)
}

// 实现接口方法
func (e Emp) PrintSalary(basic int, tax int) int {
	var salary = (basic * tax) / 100
	return basic - salary
}

func main() {
	var e1 Employee
	e1 = Emp(1)
	e1.PrintName("John Doe")
	fmt.Println("Employee Salary:", e1.PrintSalary(25000, 5))
}

如果一个类型具有接口中声明的所有方法,则不需要进一步的声明来显式表示Emp满足 Employee

声明一个以Employee作为其类型的e1变量,然后创建一个Emp值并将其分配给 e1

 

定义满足多个接口的类型

接口允许任何用户定义类型同时满足多种接口类型。

使用类型断言,您可以获取具体类型的值,并且可以调用在其他接口上定义的方法,但不是满足接口的一部分。

package main

import "fmt"

type Polygons interface {
	Perimeter()
}

type Object interface {
	NumberOfSide()
}

type Pentagon int

func (p Pentagon) Perimeter() {
	fmt.Println("Perimeter of Pentagon", 5*p)
}

func (p Pentagon) NumberOfSide() {
	fmt.Println("Pentagon has 5 sides")
}

func main() {
	var p Polygons = Pentagon(50)
	p.Perimeter()
	var o Pentagon = p.(Pentagon)
	o.NumberOfSide()

	var obj Object = Pentagon(50)
	obj.NumberOfSide()
	var pent Pentagon = obj.(Pentagon)
	pent.Perimeter()
}

当用户定义类型实现接口类型声明的方法集时,可以将用户定义类型的值分配给接口类型的值。此赋值将用户定义类型的值存储到接口值中。对接口值进行方法调用时,将执行存储的用户定义值的等效方法。由于任何用户定义类型都可以实现任何接口,因此针对接口值的方法调用本质上是多态的。此关系中的用户定义类型通常称为具体类型

 

通用方法接口

两个或多个接口可以在方法集列表中具有一个或多个常用方法。在这里,Structure是Vehicle和Human两个界面之间的常用方法。

package main

import "fmt"

type Vehicle interface {
	Structure() []string // Common Method
	Speed() string
}

type Human interface {
	Structure() []string // Common Method
	Performance() string
}

type Car string

func (c Car) Structure() []string {
	var parts = []string{"ECU", "Engine", "Air Filters", "Wipers", "Gas Task"}
	return parts
}

func (c Car) Speed() string {
	return "200 Km/Hrs"
}

type Man string

func (m Man) Structure() []string {
	var parts = []string{"Brain", "Heart", "Nose", "Eyelashes", "Stomach"}
	return parts
}

func (m Man) Performance() string {
	return "8 Hrs/Day"
}

func main() {
	var bmw Vehicle
	bmw = Car("World Top Brand")

	var labour Human
	labour = Man("Software Developer")

	for i, j := range bmw.Structure() {
		fmt.Printf("%-15s <=====> %15s\n", j, labour.Structure()[i])
	}
}

输出

ECU             <=====>           Brain
Engine          <=====>           Heart
Air Filters     <=====>            Nose
Wipers          <=====>       Eyelashes
Gas Task        <=====>         Stomach

 

接受变量地址的接口

Print()方法接受接收器指针。因此,接口还必须接受接收器指针。

如果方法接受类型值,则接口必须接收类型值;如果方法具有指针接收器,则接口必须接收相应类型的变量的地址。

package main

import "fmt"

type Book struct {
	author, title string
}

type Magazine struct {
	title string
	issue int
}

func (b *Book) Assign(n, t string) {
	b.author = n
	b.title = t
}
func (b *Book) Print() {
	fmt.Printf("Author: %s, Title: %s\n", b.author, b.title)
}

func (m *Magazine) Assign(t string, i int) {
	m.title = t
	m.issue = i
}
func (m *Magazine) Print() {
	fmt.Printf("Title: %s, Issue: %d\n", m.title, m.issue)
}

type Printer interface {
	Print()
}

func main() {
	var b Book                                 
	var m Magazine                             
	b.Assign("Jack Rabbit", "Book of Rabbits") 
	m.Assign("Rabbit Weekly", 26)              

	var i Printer
	fmt.Println("Call interface")
	i = &b    
	i.Print() 
	i = &m    
	i.Print()
}

 

空接口类型

类型接口{}称为空接口,它用于接受任何类型的值。空接口没有任何满足它所需的方法,因此每种类型都满足它。

package main

import "fmt"

func printType(i interface{}) {
	fmt.Println(i)
}

func main() {
	var manyType interface{}
	manyType = 100
	fmt.Println(manyType)

	manyType = 200.50
	fmt.Println(manyType)

	manyType = "Germany"
	fmt.Println(manyType)

	printType("Go programming language")
	var countries = []string{"india", "japan", "canada", "australia", "russia"}
	printType(countries)

	var employee = map[string]int{"Mark": 10, "Sandy": 20}
	printType(employee)

	country := [3]string{"Japan", "Australia", "Germany"}
	printType(country)
}

manyType变量被声明为类型接口 {},并且能够为其分配不同类型的值。printType()函数采用类型接口{}的参数,因此该函数可以采用任何有效类型的值。

输出

100
200.5
Germany
Go programming language
[india japan canada australia russia]
map[Mark:10 Sandy:20]
[Japan Australia Germany]

 

多态性

多态性是编写代码的能力,这些代码可以通过类型实现来承担不同的行为。

我们声明了一个名为五边形、六边形、八边形和十边形的结构,其中包含Geometry接口的实现。

package main

import (
	"fmt"
)

type Geometry interface {
	Edges() int
}

type Pentagon struct{}

type Hexagon struct{}

type Octagon struct{}

type Decagon struct{}

func (p Pentagon) Edges() int { return 5 }

func (h Hexagon) Edges() int { return 6 }

func (o Octagon) Edges() int { return 8 }

func (d Decagon) Edges() int { return 10 }

func Parameter(geo Geometry, value int) int {
	num := geo.Edges()
	calculation := num * value
	return calculation
}

func main() {
	p := new(Pentagon)
	h := new(Hexagon)
	o := new(Octagon)
	d := new(Decagon)

	g := [...]Geometry{p, h, o, d}

	for _, i := range g {
		fmt.Println(Parameter(i, 5))
	}
}

输出

25
30
40
50

我们有多态Edges()函数,它接受实现Geometry接口的值。使用多态方法,Parameter()使用传入的每个具体类型值。

 

接口嵌入

接口可以嵌入其他接口,此行为是接口多态性的一个方面,称为临时多态性

package main

import "fmt"

type Geometry interface {
	Edges() int
}

type Polygons interface {
	Geometry // 嵌入接口
}

type Pentagon int
type Hexagon int
type Octagon int
type Decagon int

func (p Pentagon) Edges() int { return 5 }
func (h Hexagon) Edges() int  { return 6 }
func (o Octagon) Edges() int  { return 8 }
func (d Decagon) Edges() int  { return 10 }

func main() {
	p := new(Pentagon)
	h := new(Hexagon)
	o := new(Octagon)
	d := new(Decagon)

	polygons := [...]Polygons{p, h, o, d}
	for i := range polygons {
		fmt.Println(polygons[i].Edges())
	}
}

当一种类型嵌入到另一种类型中时,嵌入类型的方法可用于嵌入类型。嵌入式接口的方法可供嵌入接口访问。