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]
接口的介绍就到这里。祝你有美好的一天!
原文链接: