I wrote a sample code to understand how the unexported interface works. In the below example, I have declared the unexported repoInterface in the service package.
TestRepo struct in the repo package implements the unexported repoInterface without any issues.
Code structure
repo
repo.go
service
service.go
main.go
service.go
// service/service.go
// this is the interface which the TestRepo struct implements in repo package
type repoInterface interface{
GetName() string
}
type TestService struct{
repo repoInterface
}
func NewTestService(r repoInterface) TestService {
return TestService{
repo: r,
}
}
func (s TestService) GetName() string {
return s.repo.GetName()
}
repo/repo.go
// repo/repo.go
type TestRepo struct{
name string
}
func NewTestRepo(name string) TestRepo {
return TestRepo{
name: name,
}
}
// implements repoInterface present in service package
func (r TestRepo) GetName() string {
return r.name
}
main.go
func main() {
testRepo := repo.NewTestRepo("hello")
testService := service.NewTestService(testRepo)
fmt.Println(testService.GetName())
}
// Output
// hello
My assumption so far:
This isn't possible since repo and service are different packages.
TestRepo struct present in repo package cannot implement the Unexported interface present in the service package. This is the reason why we export interfaces.
Now I realized that this is not true and my understanding is wrong.
Question:
Why does Go allow to implement an unexported interface present in a different package?
CodePudding user response:
service.NewTestService
package function requires any value that implements the type interface{ GetName() string }
.
repo
package exports a type TestRepo
which exposes a method GetName() string
.
Upon passing the repo.TestRepo
to the service.NewTestService
function like in service.NewTestService(testRepo)
, the value implements the interface by providing the expected method set.
All good.
That the type service.repoInterface
declares a not exported identifier only discriminates the packages that can use that interface name.
I have reproduced your example on the play https://go.dev/play/p/bp6z2HjwdLS
An interface type declaration containing a not exported identifier is a sealed interface.
Those sealed interfaces can not be implemented by a foreign package.
It can be a not exported method name like in
type Fooer interface {
Foo()
sealed()
}
Try here https://go.dev/play/p/3Syh7R0uS-q
It can also declare a method using a not exported argument type,
type Foo interface {
GetName() string
GetName2() sealed
}
type sealed int