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发工资了 ", p.name) } func (p pament) Pay() { fmt.Printf("%s消费了 ", p.name) }
- 例如一个普通员工除了有工资还可以去消费。分别去定义为领工资lgzer 和消费 payer接口
- 多个类型实现同一个接口
- 例如在消费时,你可以消费,其他人也可以消费,而这时必须有一个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腾讯员工消费了 ", t.name) }
- 例如在消费时,你可以消费,其他人也可以消费,而这时必须有一个pay方法
接口的内部实现
一个接口可以认定为一个远足(类型, 值)在内部表示的。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 " ,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 ", 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中有广泛的应用,但也不能任意使用接口,否则只会增加不必要的性能消耗