(七)Go语言学习笔记 - 接口(interface)

目录

  • 1 接口简单使用场景
  • 2 什么是接口
    • 2.1 details
    • 2.2 经典调用接口的实例
    • 2.3 接口和继承的关系
  • 3 多态
  • 4 断言
    • 4.1 示例
    • 4.2 示例

1 接口简单使用场景

package main

import (
	"fmt"
)
// 定义一个 Usb 接口
type Usb interface{
	Start()
	Stop()
}

// 定义手机结构体
type Phone struct{

}
// 让Phone实现Usb接口的方法
func (p Phone) Start(){
	fmt.Println("手机正在启动中......")
}
func (p Phone) Stop(){
	fmt.Println("手机正在关闭中......")
}

// 定义相机结构体
type Camera struct{

}
// 让Camera实现Usb接口的方法
func (c Camera) Start(){
	fmt.Println("相机正在启动中......")
}
func (c Camera) Stop(){
	fmt.Println("相机正在关闭中......")
}

// 定义一个Computer结构体
type Computer struct{

}
// 编写一个Working方法,接收一个Usb接口类型变量
// 只要是实现了Usb接口(所谓实现Usb接口,就是指实现了Usb接口声明的所有方法)
func (c Computer) Working(usb Usb) { // usb变量会根据传入的实参,来判断到底是Phone还是Camera

	// 通过usb接口变量来调用Start和Stop方法
	usb.Start()
	usb.Stop()

}

func main()  {

	// 测试,先创建结构体变量
	phone := Phone{}
	camera := Camera{}
	computer := Computer{}
	computer.Working(phone)
	//手机正在启动中......
	//手机正在关闭中......
	computer.Working(camera)
	//相机正在启动中......
	//相机正在关闭中......

}

2 什么是接口

interface类型可以定义一组方法,但是这些不需要实现。并且interface不能包含任何变量。到某个自定义类型(比如结构体Phone)要使用的时候,在根据具体情况把这些方法写出来(实现)。

基本语法:

	// 定义接口类型
	type 接口名 interface{
		method1(参数列表) 返回值列表
		method2(参数列表) 返回值列表
		...
	}
	
	// 实现接口所有方法
	func (t 自定义类型) method1(参数列表) 返回值列表 {
		// 方法实现
	}
	func (t 自定义类型) method2(参数列表) 返回值列表 {
		// 方法实现
	}
	...

说明:

  • (1)接口里的所有方法都没有方法体,即接口的方法都是没有实现的方法。接口体现了程序设计的多态高内聚低耦合的思想。
  • (2)Golang中的接口,不需要显示的实现。只需要一个变量,含有接口类型中的所有方法,那么这个变量就实现这个接口。因此,Golang中没有implement这样的关键字。

2.1 details

  • (1)接口本身不能创建实例,但是可以指向一个实现了该接口的自定义类型的变量(实例)
package main

import (
	"fmt"
)

type AInterface interface {
	Say()
}
type Student struct {
	Name string
}

func (stu Student) Say() {
	fmt.Println("stu Say()")
}

func main() {

	var stu Student
	var a AInterface = stu
	a.Say()  // stu Say()

}

  • (2)接口中所有的方法都没有方法体,即都是没有实现的方法
  • (3)在Golang中,一个自定义类型需要将某个接口的所有方法都实现,我们才说这个自定义类型实现了该接口。
  • (4)一个自定义类型只有实现了某个接口,才能将该自定义类型的实例(变量)赋值给接口类型
  • (5)只要是自定义数据类型,就可以实现接口,不仅仅是结构体类型。
type integer int

func (i integer) Say() {
	fmt.Println("integer i Say() =", i)
}

func main() {
	var i integer = 10
	var b AInterface = i
	b.Say() // integer i Say() = 10
}
  • (6)一个自定义数据类型可以实现多个接口
type AInterface interface {
	Say()
}

type BInterface interface{
	Hello()
}

type Monster struct{

}
func (m Monster) Hello(){
	fmt.Println("Monster hello")
}
func (m Monster) Say(){
	fmt.Println("Monster Say")
}

func main() {
	var a2 AInterface = Monster
	var b2 BInterface = Monster
	a2.Say() // Monster Say
	b2.Hello()  //Monster hello
}
  • (7)Golang接口中不能有常量
type AInterface interface{
	Name string  // 错误的写法
	Test01()
	Test02()
}
  • (8)一个接口(比如A接口)可以继承多个别的接口(比如B、C接口),这时如果要实现A接口,也必须将B、C接口的方法也全部实现。
package main

import (
	"fmt"
)

type BInterface interface {
	test01()
}

type CInterface interface {
	test02()
}

type AInterface interface {
	BInterface
	CInterface
	test03()
}

type Student struct {

}
func (stu Student) test01(){
	fmt.Println("stu test01()......")
}
func (stu Student) test02(){
	fmt.Println("stu test02()......")
}
func (stu Student) test03(){
	fmt.Println("stu test02()......")
}

func main()  {
	var stu Student
	var a AInterface = stu
	a.test01()  // stu test01()......
	a.test02()  // stu test02()......
	a.test03()  // stu test03()......

}

  • (9)interface类型默认是一个指针(引用类型),如果没有对interface初始化就使用,那么会输出nil。
  • (10)空接口interface{}没有任何方法,即所有类型都实现了空接口,我么可以把任何一个变量赋给空接口。

type T interface{

}

type Student struct {
	Name string
}

func (stu Student) Say() {
	fmt.Println("stu Say()")
}

func main() {

	var stu Student
	var t T = stu
	fmt.Println(t)
	var t2 interface{} = stu
	var num1 float64 = 8.8
	t2 = num1
	t = num1
	fmt.Println(t2, t)

}

2.2 经典调用接口的实例

实现对结构体切片的排序 sort.Sort(data Interface)

package main

import (
	"fmt"
	"sort"
	"math/rand"
)

// (1)声明一个Hero结构体
type Hero struct {
	Name string
	Age int
}

// (2)声明一个Hero结构体的切片类型
type HeroSlice []Hero

// (3)实现Interface接口
func (hs HeroSlice) Len() int {
	return len(hs)
}

// Less方法就是决定使用什么标准进行排序
func (hs HeroSlice) Less(i, j int) bool {
	// 如果i 排在j前面,那么返回true
	return hs[i].Age > hs[j].Age
}

func (hs HeroSlice) Swap(i, j int) {
	hs[i], hs[j] = hs[j], hs[i]
}

func main()  {

	var intSlice = []int{13, 1, -1, 0, 15, 56}

	//对切片进行排序
	sort.Ints(intSlice)
	fmt.Println(intSlice)  // [-1 0 1 13 15 56]

	//对结构体切片进行排序
	var heroes HeroSlice
	for i := 0; i < 10; i++{
		hero := Hero{
			Name : fmt.Sprintf("英雄~%d", rand.Intn(100)),
			Age : rand.Intn(100),
		}
		//	将hero append到heroes切片
		heroes = append(heroes, hero)
	}

	// 排序前 遍历切片
	for _, v := range(heroes){
		fmt.Println("排序前", v)
	}

	// 调用sort.Sort
	sort.Sort(heroes)
	for _, v := range(heroes){
		fmt.Println("排序后", v)
	}
}

2.3 接口和继承的关系

  • (1)接口和继承解决的问题不同
    • 继承的价值主要在于:解决代码的复用性和可维护性。
    • 接口的价值主要在于:设计,设计好各种规范(方法),让其它自定义类型去实现这些方法
  • (2)接口比继承更加灵活
    • 继承是满足 is - a的关系,而接口只需要满足 like - a的关系即可
  • (3)接口在一定程度上实现代码解耦
package main

import "fmt"

type BirdFly interface {
	Flying()
}

// 定义一个Monkey猴子结构体
type Monkey struct {
	Name string
}

func (m *Monkey) Climbing() {
	fmt.Println(m.Name, "天生会爬树...")
}

type LittleMonkey struct {
	Monkey // 继承
}

func (m *LittleMonkey) Flying() {
	fmt.Println(m.Name, "通过后天学习,会飞翔了....")
}


func main()  {

	monkey := LittleMonkey{
		Monkey{
			Name : "孙悟空",
		},
	}
	monkey.Climbing()  // 孙悟空 天生会爬树...
	monkey.Flying()

}
  • (1)A结构体继承了B结构体,那么A结构体就自动的继承了B结构体的字段和方法,并且可以直接使用
  • (2)当A结构体需要扩展功能,同时又不希望去破坏继承关系,则可以去实现某个接口即可,因此可以认为:实现接口是对继承机制的补充。

3 多态

变量(实例)具有多种形态。面向对象的第三大特征,在Go语言中,多态特征是通过接口实现的。可以按照统一的接口来调用不同的实现。这时接口变量就呈现不同的形态。
在下边的Usb 的案例就是多态的体现,usb Usb,既可以接收手机变量,又可以接收相机变量,就体现了Usb接口多态特性

  • (1)多态参数
  • (2)多态数组
package main

import (
	"fmt"
)
// 定义一个 Usb 接口
type Usb interface{
	Start()
	Stop()
}

// 定义手机结构体
type Phone struct{
	Name string
}
// 让Phone实现Usb接口的方法
func (p Phone) Start(){
	fmt.Println("手机正在启动中......")
}
func (p Phone) Stop(){
	fmt.Println("手机正在关闭中......")
}

// 定义相机结构体
type Camera struct{
	Name string
}
// 让Camera实现Usb接口的方法
func (c Camera) Start(){
	fmt.Println("相机正在启动中......")
}
func (c Camera) Stop(){
	fmt.Println("相机正在关闭中......")
}

func main()  {
	// 定义一个Usb接口数组,可以存放Phone和Camera的结构体变量
	// 这里就体现出多态数组
	var usbArr [3]Usb
	usbArr[0] = Phone{"apple"}
	usbArr[1] = Phone{"华为"}
	usbArr[2] = Camera{"Nikon"}

	fmt.Println(usbArr)  // [{apple} {华为} {Nikon}]

}

4 断言

断言引出:如何将一个接口变量类型,赋值给自定义类型的变量

package main

import "fmt"

type Point struct {
	x int
	y int
}

func main()  {

	var a interface{}
	var point Point = Point{1, 2}
	a = point

	var b Point
	// 此时如果想让a赋值给b,不能直接  b = a
	// 要用类型断言
	b = a.(Point)
	// 表示判断a是否是指向Point类型的变量,如果是就转成Point类型并赋给b变量,否则就报错
	fmt.Println(b)  // 输出 {1, 2}


	// 类型断言的其它案例
	var a1 interface{}
	var b1 float64 = 1.1
	a1 = b1
	y := a1.(float64) // 这边就不能写 a1.(float32)
	fmt.Println(y)
	// 在进行类型断言时,如果类型不匹配,就会报panic错误


	// 如何在进行断言时,带上监测机制,如果成功就ok,否则也不要报panic
	var x interface{}
	var b2 float64 = 1.1
	x = b2
	b2, ok := x.(float32)
	if ok {  // ok为true
		fmt.Println("转换成功......")
		fmt.Printf("b2 的类型是%T 值是%v", b2, b2)
	} else {
		fmt.Println("转换失败")
	}

	// 上边的判断可以简写为
	// if b2, ok := x.(float32); ok {}
	fmt.Println("继续执行")
}

4.1 示例

在之前USB实例做改进,给Phone结构体增加一个特有的方法call(),当Usb接口接收的是Phone变量时,还需要调用call方法

package main

import (
	"fmt"
)

type Usb interface {
	Start()
	Stop()
}

// 定义手机结构体
type Phone struct {
	Name string
}
func (p *Phone) Start()  {
	fmt.Println("Phone正在启动中......")
}
func (p *Phone) Stop()  {
	fmt.Println("Phone正在关闭中......")
}

func (p *Phone) Call() {
	fmt.Println("Phone正在打电话")
}

// 定义相机结构体
type Camera struct {
	Name string
}
func (c *Camera) Start()  {
	fmt.Println("Camera正在启动中......")
}
func (c *Camera) Stop()  {
	fmt.Println("Camera正在关闭中......")
}

type Computer struct {

}

func (c *Computer) Working(usb Usb) {
	usb.Start()
	// 如果usb指向Phone结构体变量,则还需要调用Call方法
	// 类型断言
	if phone, ok := usb.(*Phone); ok {
		phone.Call()
	}
	usb.Stop()
}

func main()  {

	var usbArr [3]Usb
	usbArr[0] = &Phone{"vivo"}
	usbArr[1] = &Phone{"小米"}
	usbArr[2] = &Camera{"Nikon"}

	var computer Computer
	for _, v := range usbArr {
		computer.Working(v)
		fmt.Println()
	}
	// fmt.Println(usbArr)
}

4.2 示例

package main

import (
	"fmt"
)

type Student struct {

}

// 写一个函数,循环判断传入参数的类型

func JudgeType(items... interface{}) {

	for index, x := range items {
		switch x.(type) {  // 这里type是一个关键字,固定写法
		case bool:
			fmt.Printf("第%v个参数是 bool 类型,值是 %v \n", index + 1, x)
		case float64, float32:
			fmt.Printf("第%v个参数是 浮点类型 类型,值是 %v \n", index + 1, x)
		case int, int32, int64:
			fmt.Printf("第%v个参数是 整型 类型,值是 %v \n", index + 1, x)
		case string:
			fmt.Printf("第%v个参数是 string 类型,值是 %v \n", index + 1, x)
		case Student :
			fmt.Printf("第%v个参数是 Student 类型,值是 %v \n", index + 1, x)
		case *Student :
			fmt.Printf("第%v个参数是 *Student 类型,值是 %v \n", index + 1, x)
		default:
			fmt.Println("第%v个参数是 类型不确定,值是 %v \n", index + 1, x)
		}
	}

}

func main() {
	var n1 float32 = 1.1
	var n2 float64 = 2.2
	var n3 int = 10
	var str string = "Hello"
	stu := Student{}
	stu1 := &Student{}

	JudgeType(n1, n2, n3, str, stu, stu1)
}
/*
第1个参数是 浮点类型 类型,值是 1.1
第2个参数是 浮点类型 类型,值是 2.2
第3个参数是 整型 类型,值是 10
第4个参数是 string 类型,值是 Hello
第5个参数是 Student 类型,值是 {}
第6个参数是 *Student 类型,值是 &{}
 */

你可能感兴趣的:(Golang)