golang 之 接口

interface是golang中的精华所在

定义

  接口定义了对象的行为,当一个类型为接口中的所有方法提供定义时,它被称为接口。

  具体指定类型应具有的方法,类型决定如何实现这些方法。

声明

type 接口名称 interface {
	method1(参树列表) 返回值列表
	method2(参树列表) 返回值列表
	method3(参树列表) 返回值列表
}

  interface 是为实现多态功能,意指可以根据类型的具体实现采用不同行为的能力。若一个类型实现了某个接口,所有使用这个接口的地方都可以支持这种类型。

  注意:接口通常以 er 作为名称后缀,方法名是声明组成部分,但参数名可不同或省略。如果接口没有任何方法说明,那就是一个空接口(interface{}),空接口可被赋值为任何类型的对象,接口变量默认值是nil。

type worker interface {
	job([]byte) error
}

 使用场景

  (很好的解释了为什么要使用接口) 假设公司有两个员工,一个普工,一个技术工,但是基本薪资相同,而技术工会多拿奖金。以此来计算公司为员工的总开支

package main

import "fmt"

type SalaryCaler interface {
	CalculateSalary() int
}

// 普工
type Contract struct {
	empId int
	basicpay int
}

// 技术工
type Permnanent struct {
	empId int
	basicpay int
	jiangjin int
}

func (p Permnanent) CalculateSalary() int {
	return p.basicpay + p.jiangjin
}

func (C Contract) CalculateSalary() int  {
	return C.basicpay
}

// 总开支
func totalEx(s []SalaryCaler)  {
	expense := 0
	for _, v := range s{
		expense = expense + v.CalculateSalary()
	}
	fmt.Printf("总开支 %d ", expense )
}

func main()  {
	pem1 := Permnanent{1, 3000, 10000}
	pem2 := Permnanent{2, 3000, 20000}
	cem1 := Contract{3, 3000}
	emplyees := []SalaryCaler{pem1, pem2, cem1}
	totalEx(emplyees)
}

 在上述例子中,也许会有这样的疑问:类型与接口的关系是什么样的?

类型与接口的关系

  • 一个类型实现多个接口(彼此独立)
    • 例如一个普通员工除了有工资还可以去消费。分别去定义为领工资lgzer 和消费 payer接口
      // Lgzer 接口
      type Lgzer interface {
      	say()
      }
      
      // Payer 接口
      type Payer interface {
      	move()
      }
      

       实现方法

      type pament struct {
      	name string
      }
      
      type Lgzer interface {
      	say()
      }
      
      // Payer 接口
      type Payer interface {
      	move()
      }
      
      func (p pament) Lgz() {
      	fmt.Printf("%s发工资了\n", p.name)
      }
      
      func (p pament) Pay() {
      	fmt.Printf("%s消费了\n", p.name)
      }
  • 多个类型实现同一个接口
    • 例如在消费时,你可以消费,其他人也可以消费,而这时必须有一个pay方法
      type Payer interface {
      	pay()
      }
      

       具体实现如

      type Payer interface {
      	pay()
      }
      
      type ali struct {
      	name string
      }
      
      type tenchrt struct {
      	name string
      }
      
      func (a ali) pay() {
      	fmt.Printf("%s 员工消费了", a.name)
      }
      
      func (t tenchrt) pay() {
      	fmt.Printf("%s腾讯员工消费了\n", t.name)
      }
      

接口的内部实现

   一个接口可以认定为一个远足(类型, 值)在内部表示的。type是接口的基础具体类型, value是具体类型的值

package main

import "fmt"

type Test interface {
	Tester()
}

type MyFloat float64

func (m MyFloat) Tester()  {
	fmt.Println(m)
}

func describe(t Test)  {
	fmt.Printf("interface 类型%T, 值: %v\n" ,t, t)
}

func main()  {
	var t Test
	f := MyFloat(90.1)
	t = f
	describe(t)
	t.Tester()
}

 打印结果

interface 类型main.MyFloat, 值: 90.1
90.1

空接口

一开始就说过空接口,那么什么是空接口,即具有0个方法的接口称为空接口,它表示为interface{},由于没有方法,所以所有类型都能实现空接口。

package main

import "fmt"

func describe(i interface{})  {
	fmt.Printf("Type = %T, value = %v \n", i, i)
}

func main()  {
	s := "hello"
	i := 55
	start := struct {
		nane string
	}{
		"h",
	}
	describe(s)
	describe(i)
	describe(start)
}

 输出对应结果

Type = string, value = hello 
Type = int, value = 55 
Type = struct { nane string }, value = {h} 

 有了空接口即会存在类型不确定的状态,这时就会有类型断言

类型断言

类型断言用于提取接口接口的基础值,语法 i.(T)

  • i:表示类型为interface{}的变量
  • T:表示断言 i 可能是的类型。

一个接口值是由一个具体类型和具体类型的值来组成,有分别称为接口的动态类型。图分解如下

接口值图解

具体如

package main

import "fmt"

func findType(i interface{})  {
	switch v := i.(type) {
	case string:
		fmt.Println("v is string", v)
	case int:
		fmt.Println("v is int", v)
	case float64:
		fmt.Println("v is float", v)
	default:
		fmt.Println("不属于当前类型")
	}
}

func main()  {
	findType("name")
	findType(90.2)
	findType(30)
}  

 虽然空接口在go中有广泛的应用,但也不能任意使用接口,否则只会增加不必要的性能消耗

 

posted @ 2020-02-23 17:00  Dwyane.wang  阅读(315)  评论(0编辑  收藏  举报