golang接口2
用指针接收者实现接口
在接口-I中所有例子都是以值为接收者。也可以使用指针接收者来实现接口。让我们通过一个程序看看这是如何做到的。
packagemain import"fmt" typeDescriberinterface{ Describe() } typePersonstruct{ namestring ageint } func(pPerson)Describe(){//implementedusingvaluereceiver fmt.Printf("%sis%dyearsold\n",p.name,p.age) } typeAddressstruct{ statestring countrystring } func(a*Address)Describe(){//implementedusingpointerreceiver fmt.Printf("State%sCountry%s",a.state,a.country) } funcmain(){ vard1Describer p:=Person{"Sam",25} d1=p d1.Describe() vard2Describer a:=Address{"Washington","USA"} /*compilationerrorifthefollowinglineis uncommented cannotusea(typeAddress)astypeDescriber inassignment:Addressdoesnotimplement Describer(Describemethodhaspointer receiver) */ //d2=a ap:=&a d2=ap//ThisworkssinceDescriberinterface //isimplementedbyAddresspointerinline22 d2.Describe() }12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
在上面的程序中,第13行,Person结构体以值作为接收者实现了Describer接口,而在第22行,Address结构体以指针作为接收者实现了Describer接口。
如果将第42行的注释去掉,我们将得到一个编译错误:main.go:42:cannotusea(typeAddress)astypeDescriberinassignment:AddressdoesnotimplementDescriber(Describemethodhaspointerreceiver)。这是因为,在第22行我们使用的是Address指针作为接收者来实现Describer接口,但是我们试图将一个没有实现Describer的接口的值类型的变量a赋值给接口变量d2。第45行是合法的,因为我们将指向a的指针ap赋值给d2。
程序剩下的部分不言自明。程序的输出如下:
Samis25yearsold
StateWashingtonCountryUSA12
实现多个接口
一个类型可以实现多个接口。让我们通过下面的程序看看这是如何做到的。
packagemain import( "fmt" ) typeSalaryCalculatorinterface{ DisplaySalary() } typeLeaveCalculatorinterface{ CalculateLeavesLeft()int } typeEmployeestruct{ firstNamestring lastNamestring basicPayint pfint totalLeavesint leavesTakenint } func(eEmployee)DisplaySalary(){ fmt.Printf("%s%shassalary$%d",e.firstName,e.lastName,(e.basicPay+e.pf)) } func(eEmployee)CalculateLeavesLeft()int{ returne.totalLeaves-e.leavesTaken } funcmain(){ e:=Employee{ firstName:"Naveen", lastName:"Ramanathan", basicPay:5000, pf:200, totalLeaves:30, leavesTaken:5, } varsSalaryCalculator=e s.DisplaySalary() varlLeaveCalculator=e fmt.Println("\nLeavesleft=",l.CalculateLeavesLeft()) }123456789101112131415161718192021222324252627282930313233343536373839404142434445
上面的程序在第7行和第11行分别声明了两个接口SalaryCalculator和LeaveCalculator。
结构体Employee(定义在第15行)在第24行实现了SalaryCalculator接口的DisplaySalary方法,而在第28行实现了LeaveCalculator接口的CalculateLeavesLeft方法。现在Employee同时实现了SalaryCalculator和LeaveCalculator两个接口。
在第41行我们将e赋值给SalaryCalculator类型的变量,在第43行我们将同样的变量e赋值给了LeaveCalculator类型的变量。这是合法的,因为e的类型是Employee,而Employee实现了SalaryCalculator和LeaveCalculator两个接口。
程序的输出为:
NaveenRamanathanhassalary$5200
Leavesleft=2512
接口的嵌套
虽然Go没有提供继承机制,但是仍然可以通过嵌入其他接口的方式创建一个新的接口。
下面的程序说明了这一点。
packagemain import( "fmt" ) typeSalaryCalculatorinterface{ DisplaySalary() } typeLeaveCalculatorinterface{ CalculateLeavesLeft()int } typeEmployeeOperationsinterface{ SalaryCalculator LeaveCalculator } typeEmployeestruct{ firstNamestring lastNamestring basicPayint pfint totalLeavesint leavesTakenint } func(eEmployee)DisplaySalary(){ fmt.Printf("%s%shassalary$%d",e.firstName,e.lastName,(e.basicPay+e.pf)) } func(eEmployee)CalculateLeavesLeft()int{ returne.totalLeaves-e.leavesTaken } funcmain(){ e:=Employee{ firstName:"Naveen", lastName:"Ramanathan", basicPay:5000, pf:200, totalLeaves:30, leavesTaken:5, } varempOpEmployeeOperations=e empOp.DisplaySalary() fmt.Println("\nLeavesleft=",empOp.CalculateLeavesLeft()) }12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
在上面的程序第15行,通过嵌入SalaryCalculator和LeaveCalculator两个接口的方式创建了新的接口EmployeeOperations。
任何一个实现了SalaryCalculator和LeaveCalculator两个接口的方法的类型,也实现了EmployeeOperations接口。
Employee结构体实现了EmployeeOperations接口,因为它在第29行和第33行分别提供了DisplaySalary和CalculateLeavesLeft的方法。
在第46行,Employee类型的e被赋值给EmployeeOperations类型的empOp。在下面两行,以empOp作为参数调用DisplaySalary()和CalculateLeavesLeft()方法。
程序的输出为:
NaveenRamanathanhassalary$5200
Leavesleft=2512
接口的0值
接口的0值是nil。一个nil接口的底层类型和值都是nil。
packagemain import"fmt" typeDescriberinterface{ Describe() } funcmain(){ vard1Describer ifd1==nil{ fmt.Printf("d1isnilandhastype%Tvalue%v\n",d1,d1) } } 123456789101112131415
上面的程序中,d1是nil,程序的输出为:
d1isnilandhastype<nil>value<nil>1
如果我们试图在一个nil接口上调用方法,程序将会触发panic,因为nil接口既没底层的值,也没有具体的类型。
packagemain typeDescriberinterface{ Describe() } funcmain(){ vard1Describer d1.Describe() }12345678910
上面的程序中,因为d1是nil。程序将在运行时触发panic:
panic:runtimeerror:invalidmemoryaddressornilpointerdereference [signalSIGSEGV:segmentationviolationcode=0xffffffffaddr=0x0pc=0xc8527]
接口的介绍就到这里。祝你有美好的一天!
原文链接: