zoukankan      html  css  js  c++  java
  • Go 接口(interface)

        文章转载地址:https://www.flysnow.org/2017/04/03/go-in-action-go-interface.html

    1.什么是 interface?

       简单的说,interface 是一组 method 签名的组合,通过 interface 定义对象的一组行为

      上一篇文章中我们实现了 Student 和 Employee 都能 SayHi,现在我们进一步做扩展,Student 和 Employee 

    实现另一个方法 Sing,然后 Student 实现方法 BorrowMoney 而 Employee 实现 SpendSalary

      这样,Student 实现了三个方法:SayHi,Sing,BorrowMoney ;而 Employee 实现了 SayHi,Sing, SpendSalary

      上面这些方法的组合被称为 interface(被对象 Student 和 Employee 实现)。例如:Student 和 Employee 都实现了 interface:

    SayHi,Sing,也就是这两个对象是该 interface 类型。而 Employee 没有实现这个 interface:SayHi,Sing,BorrowMoney,

    是因为 Employee 没有实现 BorrowMoney 这个方法

    2. interface 类型

      interface 定义了一组方法,如果某个对象实现了某个接口的所有方法,则此对象就实现了这个接口

    package main
    
    import "fmt"
    
    // 定义 Human 结构体
    type Human struct {
    	name string
    	age int
    	phone string
    }
    
    // 定义 Student 结构体
    type Student struct {
    	Human // 匿名字段
    	company string
    	loan  float32
    }
    
    // 定义结构体 Employee
    type Employee struct {
    	Human // 匿名字段
    	company string
    	money float32
    }
    
    // Human 实现 SayHi 方法
    func (h *Human) SayHi() {
    	fmt.Printf("Hi, I am %s you can call me on %s
    ", h.name, h.phone)
    }
    
    // Human 实现 Sing 方法 传入 lyrics(歌词) 参数
    func (h *Human) Sing(lyrics string)  {
    	fmt.Println("La la, la la la, la la la la la...", lyrics)
    }
    
    // Human 实现 Guzzle 方法
    func (h *Human) Guzzle(beerStein string) {
    	fmt.Println("Guzzle Guzzle Guzzle...", beerStein)
    }
    
    // Employee 重载 Human 的 SayHi 方法
    func (e *Employee) SayHi() {
    	fmt.Printf("Hi, I am %s, I work at %s. Call me on %s
    ", e.name,
    		e.company, e.phone)
    }
    
    // Student 实现 BorrowMoney 方法
    func (s *Student) BorrowMoney(amount float32) {
    	s.loan += amount
    }
    
    // Employee 实现 SpendSalary 方法
    func (e *Employee) SpendSalary(amount float32)  {
    	e.money -= amount
    }
    
    // 定义 interface
    type Men interface {
    	SayHi()
    	Sing(lyrics string)
    	Guzzle(beerStein string)
    }
    
    type YoungChap interface {
    	SayHi()
    	Sing(lyrics string)
    	BorrowMoney(amount float32)
    }
    
    type ElderlyGent interface {
    	SayHi()
    	Sing(lyrics string)
    	SpendSalary(amount float32)
    }
    
    func main() {
    
    }
    

      通过上面的代码我们可以知道,interface 可以被任意的对象实现。我们看到上面的 Men interface 被 Human、Student、Employee

    实现(一个接口可以被多个对象实现)。同理,一个对象可以实现任意多个接口,例如上面的 Student 实现了 Men 和 YoungChap 两个interface

        最后,任意的类型都实现了空接口(interface{})

    3. interface 值

        

    package main
    
    import "fmt"
    
    // 定义 Human 结构体
    type Human struct {
    	name string
    	age int
    	phone string
    }
    
    // 定义 Student 结构体
    type Student struct {
    	Human // 匿名字段
    	company string
    	loan  float32
    }
    
    // 定义结构体 Employee
    type Employee struct {
    	Human // 匿名字段
    	company string
    	money float32
    }
    
    // Human 实现 SayHi 方法
    func (h Human) SayHi() {
    	fmt.Printf("Hi, I am %s you can call me on %s
    ", h.name, h.phone)
    }
    
    // Human 实现 Sing 方法 传入 lyrics(歌词) 参数
    func (h Human) Sing(lyrics string)  {
    	fmt.Println("La la, la la la, la la la la la...", lyrics)
    }
    
    
    // Employee 重载 Human 的 SayHi 方法
    func (e Employee) SayHi() {
    	fmt.Printf("Hi, I am %s, I work at %s. Call me on %s
    ", e.name,
    		e.company, e.phone)
    }
    
    // Interface Men 都被 Human、Student、Employee 实现
    type Men interface {
    	SayHi()
    	Sing(lyrics string)
    }
    
    func main() {
    	mike := Student{Human{"Mike",25,"222-222-xxx"},"MIT",0.00}
    	paul := Student{Human{"Paul",26,"111-222-xxx"},"Harvard",100}
    
    	sam := Employee{Human{"Sam",36,"444-222-xxx"},"Golang Inc.",1000}
    	tom := Employee{Human{"Tom",37,"222-444-xxx"},"Things Ltd.",5000}
    
    
    	// 定义 Men 类型的 i
    	var i Men
    
    	// i 能存储 Student
    	i = mike
    	fmt.Println("This is Mike,a Student:")
    	i.SayHi()
    	i.Sing("November rain")
    
    	// i 也能存储 Employee
    	i = tom
    	fmt.Println("This is tom, an Employee:")
    	i.SayHi()
    	i.Sing("Born to be wild")
    
    	// 定义slice Men
    	fmt.Println("Let's use a slice of Men and see what happens")
    	x := make([]Men,3)
    
    	// 这三个都是不同类型的元素,但是他们实现了interface同一个接口
    	x[0],x[1],x[2] = paul,sam,mike
    
    	for _,value := range x{
    		value.SayHi()
    	}
    }
    
    --------------------------------------------------------------------------
    
    输出结果:
    
    This is Mike,a Student:
    Hi, I am Mike you can call me on 222-222-xxx
    La la, la la la, la la la la la... November rain
    This is tom, an Employee:
    Hi, I am Tom, I work at Things Ltd.. Call me on 222-444-xxx
    La la, la la la, la la la la la... Born to be wild
    Let's use a slice of Men and see what happens
    Hi, I am Paul you can call me on 111-222-xxx
    Hi, I am Sam, I work at Golang Inc.. Call me on 444-222-xxx
    Hi, I am Mike you can call me on 222-222-xxx
    

    4.空 interface 

      空 interface(interface{}) 不包含任何的 method,正因为如此,所有类型都实现了空 interface。空 interface 对于描述

    起不到任何作用(因为它不包含任何的 method),但是空 interface 在我们需要存储任意类型的数值的时候相当有用,因

    为它可以存储任意类型的数值。如下示例:

    // 定义 a 为空接口
    var a interface{}
    var i int = 5
    
    s := "Hello world'
    
    // a 可以存储任意类型的数值
    a = i
    a = s
    

     一个函数把 interface{} 作为参数,则可以接受任意类型的值作为参数,如果一个函数返回 interface{} ,那么也就可以

    返回任意类型的值

    5.interface 变量存储的类型

      我们知道  interface 的变量里面可以存储任意类型的数值(该类型实现了 interface),那么我们如何反向知道这个变量里面实际保存

    的是哪个类型的对象?目前有两种方式:

      5.1 Comma-ok 断言

            Go 语言里面有一个语法,可以直接判断是否是该类型的变量:value,ok = element.(T),这里 value 就是变量的值,ok 是一个

    bool 类型,element 是 interface 变量,T 是断言的类型

            如果 element 里面确实存储了 T 类型的数值,那么 ok 返回 true,否则返回 false

            如下示例:

    package main
    
    import (
    	"fmt"
    	"strconv"
    )
    
    // 定义一个空接口
    type Eelement interface {}
    
    type List [] Eelement
    
    // 定义一个 Person 结构体
    type Person struct {
    	name string
    	age int
    }
    
    // 给 Person 绑定一个方法
    func (p Person) String() string{
    	return "(name: " + p.name + " - age: "+strconv.Itoa(p.age)+ " years)"
    }
    
    func main() {
    	list := make(List,3)
    	list[0] = 1 // an int
    	list[1] = "Hello" // a string
    	list[2] = Person{"Dennis",70}
    
    	for index,element := range list{
    		// 类型判断
    		if value,ok := element.(int);ok{
    			fmt.Printf("list[%d] is an int and its value is %d
    ", index, value)
    		}else if value,ok := element.(string);ok{
    			fmt.Printf("list[%d] is a string and its value is %s
    ", index, value)
    		}else if value,ok := element.(Person);ok{
    			fmt.Printf("list[%d] is a Person and its value is %s
    ", index, value)
    		}else{
    			fmt.Printf("list[%d] is of a different type
    ", index)
    		}
    	}
    }
    ----------------------------------------------------------------------------------
    
    输出结果:
    
    list[0] is an int and its value is 1
    list[1] is a string and its value is Hello
    list[2] is a Person and its value is (name: Dennis - age: 70 years)
    

      5.2 type-switch 

                 直接看示例:

    package main
    
    import (
    	"fmt"
    	"strconv"
    )
    
    // 定义一个空接口
    type Eelement interface {}
    
    type List [] Eelement
    
    // 定义一个 Person 结构体
    type Person struct {
    	name string
    	age int
    }
    
    // 给 Person 绑定一个方法
    func (p Person) String() string{
    	return "(name: " + p.name + " - age: "+strconv.Itoa(p.age)+ " years)"
    }
    
    func main() {
    	list := make(List,3)
    	list[0] = 1 // an int
    	list[1] = "Hello" // a string
    	list[2] = Person{"Dennis",70}
    
    	for index,element := range list{
    		// 使用 type-switch 做类型判断
    		switch value := element.(type) {
    		case int:
    			fmt.Printf("list[%d] is an int and its value is %d
    ", index, value)
    		case string:
    			fmt.Printf("list[%d] is a string and its value is %s
    ", index, value)
    		case Person:
    			fmt.Printf("list[%d] is a Person and its value is %s
    ", index, value)
    		default:
    			fmt.Println("list[%d] is of a different type", index)
    		}
    	}
    }
    
    -------------------------------------------------------------------------------------
    
    输出结果:
    
    list[0] is an int and its value is 1
    list[1] is a string and its value is Hello
    list[2] is a Person and its value is (name: Dennis - age: 70 years)
    

    6. 嵌入 interface

        如果一个 interface1 作为 interface2 的一个嵌入字段,那么 interface2 隐式的包含了 interface1 里面的method

       在源码包 container/heap 里面有这样的一个定义:

    type Interface interface {
    	sort.Interface
    	Push(x interface{}) // add x as element Len()
    	Pop() interface{}   // remove and return element Len() - 1.
    }
    

     看如上代码片段,我们看到 sort.Interface 其实就是嵌入字段,把 sort.Interface 的所有 method 给隐式的包含进来了,

    即下面的方法:

    type Interface interface {
    	// Len is the number of elements in the collection.
    	Len() int
    	// Less reports whether the element with
    	// index i should sort before the element with index j.
    	Less(i, j int) bool
    	// Swap swaps the elements with indexes i and j.
    	Swap(i, j int)
    }
    

      另外一个就是 io 包下面的 io.ReadWriter ,它包含了 io 包下面的两个 interface:Reader、Writer

    // ReadWriter is the interface that groups the basic Read and Write methods.
    type ReadWriter interface {
    	Reader
    	Writer
    }
    

     下面再来看一个示例:

    package main
    
    import "fmt"
    
    // 定义一个 USB interface
    type USB interface {
    	Name() string
    	// interface 嵌入
    	Connecter
    }
    
    // 定义一个 Connecter interface
    type Connecter interface {
    	Connect()
    }
    
    // 定义一个 struct
    type PhoneConnecter struct {
    	name string
    }
    
    // 给 PhoneConnecter 绑定 Name 方法
    func (pc PhoneConnecter) Name() string{
    	return pc.name
    }
    
    // 绑定 Connect 方法
    func (pc PhoneConnecter) Connect() {
    	fmt.Println("Connected:",pc.name)
    }
    
    func main() {
    	a := PhoneConnecter{"PhoneConnecter"}
    	a.Name()
    	a.Connect()
    }
    

      

  • 相关阅读:
    Macaca拓展自己控件的方法
    pyparsing:定制自己的解析器
    利用 Traceview 精准定位启动时间测试的异常方法 (工具开源)
    Jenkins插件开发
    一个简陋的高并发请求脚本的演进过程
    python 自定义回调函数
    默认端口
    基本数据类型封装类比较
    grep命令和tail命令
    idea 拉取git新分支
  • 原文地址:https://www.cnblogs.com/leeyongbard/p/10371774.html
Copyright © 2011-2022 走看看