接口实现多态

[TOC]

😶‍🌫️go语言官方编程指南:https://pkg.go.dev/stdopen in new window

go语言的官方文档学习笔记很全,推荐去官网学习

😶‍🌫️我的学习笔记:github: https://github.com/3293172751/golang-rearnopen in new window


区块链技术(也称之为分布式账本技术),是一种互联网数据库技术,其特点是去中心化,公开透明,让每一个人均可参与的数据库记录。

❤️💕💕关于区块链技术,可以关注我,共同学习更多的区块链技术。博客http://nsddd.topopen in new window

接口(interface)

接口注意:

Golang中的接口也是一种数据类型,不需要显示实现。只需要一个变量含有接口类型中的所有方法,那么这个变量就实现了这个接口。

接口是一种规范,使用接口必须要按照它的规范来。

可以想象usb是现实中的接口,同时usb可以作为多种不同的尺寸和排线,这种设计需求在golang中也是大量存在的

按照循序应该是多态,但是在讲解多态之前需要讲解接口,因为在Golang中,多态的特性主要是通过接口来体现出来的

接口快速入门

接口💡简单的一个案例如下:

快速了解接口:

package main
import (
	"fmt"
)

//声明/定义一个接口
type Usb interface {
	//声明了两个没有实现的方法
	Start() 
	Stop()
}

//声明/定义一个接口
type Usb2 interface {
	//声明了两个没有实现的方法
	Start()
	Stop()
	Test()
}

//定义手机的结构体
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("相机停止工作。。。")
}

//计算机
type Computer struct {

}

//编写一个方法Working 方法,接收一个Usb接口类型变量
//只要是实现了 Usb接口 (所谓实现Usb接口,就是指实现了 Usb接口声明所有方法)
func (c Computer) Working(usb Usb) {
	//通过usb接口变量来调用Start和Stop方法
	usb.Start()
	usb.Stop()
}

func main() {
	//测试
	//先创建结构体变量
	computer := Computer{}
	phone := Phone{}
	camera := Camera{}

	//关键点
	computer.Working(phone)
	computer.Working(camera) //实现camera
}

🚀 编译结果如下:

[Running] go run "d:\文档\最近的\awesome-golang\docs\code\go-super\26-main.go"
手机开始工作。。。
手机停止工作。。。
相机开始工作~~~。。。
相机停止工作。。。

在文档里面接口的文档也是非常多的

接口概念

接口定义

接口类型可以定义一组方法,但是这些不需要实现,而且Interface不能包含任何的变量

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] {
   /* 方法实现*/
}

小结说明:

  1. 接口中所有的方法都没有方法体,即接口的方法都是没有实现的方法。接口体现了程序设计的多态和高内聚低耦合的思想

  2. Golang中不需要显式存在,只要一个变量,含有接口类型中的所有方法,那么叫做这个变量实现了这个接口,

  3. 接口本身不能创建实例,但是它可以指向一个实现了该接口的自定义类型的变量

一个自定义类型可以实现多个接口

type BInterface interface{
	Say()
}
type AInterface interface{
	Hello()
}

type Monster struct{
//想让接口体monster即实现B接口也实现A接口
	
}
func (m monstall) Hello(){
	fmt.Println("monstall hello()~~")
}

func (m monstall) Say(){
	fmt.Println("monstall say()~~")
}

此时此刻monstall实现了Ainterface and Binterface

func main(){
    var monster Monster 
    var Atow AInterface = monster
    var Btow AInterface = monster
    Atow.Say()
    Btow.Hello()
}

Golang接口中不可以有任何的变量

type AInterface interface{
	int          //报错
	Hello()
}

空接口

空接口表示没有任何约束,任何类型都可以实现空接口

⚡ 所有有时候需要任何类型的可以选择空接口~

//空接口表示任意类型
func main() {
    var a interface{}
    a = 20
    a = "你好hello"
    a = true
}

map定义空接口:

	var m1 = make(map[string]interface{})
	m1["username"] = userinfo["username"]
	m1["password"] = userinfo["password"]
	m1["age"] = userinfo["age"]
	m1["hobby"] = 123
	fmt.Println("m1=", m1)
	fmt.Printf("userinfo[\"username\"] = %T", userinfo["username"])

切片定义空接口

var slice1 = []interface{}{1, 2, 3, 4, 5, "hello", true}
	fmt.Printf("a type is %T, a value is %v\n", slice1, slice1) 
//a type is []interface {}, a value is [1 2 3 4 5 hello true]
空接口的一些实现

⚡ 空接口有一些实现的方案:

/*
 * @Description: null interface
 * @Author: xiongxinwei 3293172751nss@gmail.com
 * @Date: 2022-10-04 21:37:41
 * @LastEditTime: 2022-10-25 09:11:10
 * @FilePath: \code\go-super\27-main.go
 * @Github_Address: https://github.com/cubxxw/awesome-cs-cloudnative-blockchain
 * Copyright (c) 2022 by xiongxinwei 3293172751nss@gmail.com, All Rights Reserved. @blog: http://nsddd.top
 */
package main

import "fmt"

//空接口
type A interface{}

func show(a interface{}) {
	fmt.Printf("type:%T value:%v", a, a) //type:interface {} value:100
}

func main() {
	var a A
	b := "hello"
	a = b                //表示a可以接收任意类型的数据
	fmt.Println("a=", a) //a= hello

	var num = 20
	a = num //表示空接口可以接收任意类型的数值
	fmt.Printf("a type is %T, a value is %v\n", a, a)

	var stu = struct {
		name string
		age  int
	}{
		name: "tom",
		age:  20,
	}

	a = stu
	fmt.Printf("a type is %T, a value is %v\n", a, a) //a type is struct { name string; age int }, a value is {tom 20}

	show(100) //type:int value:100

	//fmt.Println("a.name=", a.name) //空接口没有字段,不能直接访问字段"
}

🚀 编译结果如下:

[Running] go run "d:\文档\最近的\awesome-golang\docs\code\go-super\27-main.go"
a= hello
a type is int, a value is 20
a type is struct { name string; age int }, a value is {tom 20}
type:int value:100

接口之间也可以有继承的关系(比如AInterface可以继承BInterface and CInterface)

/*************************************************************************           
    > File Name: Interface.go
    > Author: smile
    > Mail: 3293172751nss@gmail.com 
    > Created Time: Sat 05 Mar 2022 01:36:23 PM CST
 ************************************************************************/
package main
import (
    "fmt"
)

type BInterface interface {
    test01()      //定义test01一个方法
}

type CInterface interface {
    test02()
}

type AInterface interface {
    //at AInterface comprise BInterface and CInterface and it has its own way test03
    BInterface
    CInterface
    test03()     //意味着如果要实现Ainterface 就要实现下面的所有方法
}

//如果需要实现AInterface,就需要将BInterface CInterface的方法都实现
type Stu struct {
}

func (stu Stu) test01() {

}
func (stu Stu) test02() {
    
}
func (stu Stu) test03() {
    
}
//只有将三个方法全部实现了,那么stu就实现了AInterface 缺一不可!!!!!!!!
type T  interface{

}

func main() {
    var stu Stu 
    var a AInterface = stu 
    a.test01()

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

接口是引用类型~,传值的时候是以引用方式(地址)传送进去的

空接口interface{}没有任何的方法所有的类型都实现了空接口,我们可以把任何的变量赋值给空接口

type T  interface{
}

func main() {
    var t T = stu //ok
    fmt.Println(t)
    
    //也可以这样写:
    var t2 interface{}  = stu      
    var num1 float64 = 8.8 
    t2 = num1         //可以直接将num1赋值
    t = num1
    fmt.Println(t2, t)
}

编译:

[root@mail golang]# go build -o Interface Interface.go 
[root@mail golang]# ./Interface 
{}
8.8 8.8

注意继承的时候,接口之间不可以有相同的方法名

type BInterface interface {
    test01()      //定义test01一个方法
    test02()
}

type CInterface interface {
    test02()
    test03()
}      /*---正常编译---*/

type AInterface interface {
	AInterface
	BInterface
}      /*---编译错误---*/
func main(){
 /*--报错:重复定义--*/   
}

接口实践

常见报错,接口是属于引用传递

package main
import "fmt"
type Usb interface {
	Say()
}
type Stu struct {
}
func (this *Stu) Say() {
	fmt.Println("Say()")
}
func main() {
	var stu Stu = Stu{}
	// 错误! 会报 Stu类型没有实现Usb接口 , 
	// 如果希望通过编译,  var u Usb = &stu
	var u Usb = stu  
	u.Say()
	fmt.Println("here", u)
}

接口实现对Hero结构体切片的排序

实现对Hero结构体切片的排序

思想:使用冒泡排序也可以使用系统提供的方法

💡简单的一个案例如下:

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方法就是决定你使用什么标准进行排序
//1. 按Hero的年龄从小到大排序!!
func (hs HeroSlice) Less(i, j int) bool {
	return hs[i].Age < hs[j].Age
	//修改成对Name排序
	//return hs[i].Name < hs[j].Name
}

func (hs HeroSlice) Swap(i, j int) {
	//交换
	// temp := hs[i]
	// hs[i] = hs[j]
	// hs[j] = temp
	//下面的一句话等价于三句话
	hs[i], hs[j] = hs[j], hs[i]
}


//1.声明Student结构体
type  Student struct{
	Name string
	Age int
	Score float64
}

//将Student的切片,安Score从大到小排序!!

func main() {

	//先定义一个数组/切片
	var intSlice = []int{0, -1, 10, 7, 90}
	//要求对 intSlice切片进行排序
	//1. 冒泡排序...
	//2. 也可以使用系统提供的方法 
	sort.Ints(intSlice) 
	fmt.Println(intSlice)

	//请大家对结构体切片进行排序
	//1. 冒泡排序...
	//2. 也可以使用系统提供的方法

	//测试看看我们是否可以对结构体切片进行排序
	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)
	fmt.Println("-----------排序后------------")
	//看看排序后的顺序
	for _ , v := range heroes {
		fmt.Println(v)
	}

	i := 10
	j := 20
	i, j = j, i
	fmt.Println("i=", i, "j=", j) // i=20 j = 10
}

接口VS继承

举例

如何理解继承和接口?

猴子如果学猴子,那就是继承,如果猴子想学鸟飞,学鱼游,就是接口

接口相当于是对继承的补充

package main
import(
	"fmt"
)
type Monkey struct{
	Name string 
}

func (this *Monkey) climbing(){
	fmt.Println(this.Name,"生来会爬树..")
}

//Little Monkey结构体
type LittleMonkey struct{
	Monkey   //匿名结构体 
    //继承
}
func main(){
	//创建一个littleMonkey 实例
    monkey := LittleMonkey{
        Monkey{
            Name : "悟空",
        },
    }
    monkey.climbing()
}
[root@mail ~]# go run monkey.go 
悟空 生来会爬树..

*如果猴子想学飞????☆: .。. o(≧▽≦)o .。.:*☆ **

//声明接口
type BirdAble interface{
	Flying()
}
type BirdAble interfacpackage main
import (
	"fmt"
)

//Monkey结构体
type Monkey struct {
	Name string
}

//声明接口
type BirdAble interface {
	Flying()
}

type FishAble interface {
	Swimming()
}

func (this *Monkey) climbing() {
	fmt.Println(this.Name, " 生来会爬树..")
}

//LittleMonkey结构体
type LittleMonkey struct {
	Monkey //继承
}


//让LittleMonkey实现BirdAble
func (this *LittleMonkey) Flying() {
	fmt.Println(this.Name, " 通过学习,会飞翔...")
}

//让LittleMonkey实现FishAble
func (this *LittleMonkey) Swimming() {
	fmt.Println(this.Name, " 通过学习,会游泳..")
}

func main() {

	//创建一个LittleMonkey 实例
	monkey := LittleMonkey{
		Monkey {
			Name : "悟空",
		},
	}
	monkey.climbing()
	monkey.Flying()
	monkey.Swimming()

}e{
	Flying()
}
type Monkey struct{
	Name string 
}

func (this *Monkey) climbing(){
	fmt.Println(this.Name,"生来会爬树..")
}

func (this *LittleMonkey) Swimming(){
	fmt.Println(this.Name,"通过学习会游泳..")
}
//Little Monkey结构体
type LittleMonkey struct{
	Monkey   //匿名结构体 
    //继承
}
func main(){
	//创建一个littleMonkey 实例
    monkey := LittleMonkey{
        Monkey{
            Name : "悟空",
        },
    }
    monkey.climbing()
    monkey.Swimming()
}

实现接口可以看作是对继承的一种补充

接口和继承解决的问题不同

继承的主要价值在于:解决问题的复用性和可维护性

接口的主要价值在于:设计,设计好各种规范(方法),让其他自定义类型去是实现这些方法

接口比继承更加灵活

接口是比继承更加灵活的

继承是满足is - a的关系,而接口只需要满足like - a的关系

提示

接口在一定程度上实现代码解耦

END 链接