英文:
Why does Go allow a struct to implement an unexported interface present in a different package?
问题
我写了一段示例代码来理解未导出的接口的工作原理。在下面的示例中,我在service包中声明了未导出的repoInterface。
repo包中的TestRepo结构体实现了未导出的repoInterface**,没有任何问题。
代码结构如下:
repo
repo.go
service
service.go
main.go
service.go
// service/service.go
// 这是TestRepo结构体在repo包中实现的接口
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,
}
}
// 实现了service包中的repoInterface接口
func (r TestRepo) GetName() string {
return r.name
}
main.go
func main() {
testRepo := repo.NewTestRepo("hello")
testService := service.NewTestService(testRepo)
fmt.Println(testService.GetName())
}
// 输出
// hello
我目前的假设:
> 这是不可能的,因为repo和service是不同的包。
> repo包中的TestRepo结构体无法实现service包中的未导出接口。
> 这就是为什么我们要导出接口的原因。
现在我意识到这是错误的,我的理解是错误的。
问题:
为什么Go允许在不同包中实现未导出的接口?
英文:
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?
答案1
得分: 2
service.NewTestService
包的函数需要实现interface{ GetName() string }
类型的任何值。
repo
包导出了一个TestRepo
类型,该类型暴露了一个GetName() string
方法。
当将repo.TestRepo
传递给service.NewTestService
函数,如service.NewTestService(testRepo)
,该值通过提供预期的方法集来实现接口。
一切都很好。
service.repoInterface
类型声明了一个未导出的标识符,这只是区分可以使用该接口名称的包。
我在playground上复制了你的示例:https://go.dev/play/p/bp6z2HjwdLS
包含未导出标识符的接口类型声明是一个封闭接口。
这些封闭接口不能由外部包实现。
可以是未导出的方法名,例如:
type Fooer interface {
Foo()
sealed()
}
在这里尝试:https://go.dev/play/p/3Syh7R0uS-q
它还可以声明使用未导出的参数类型的方法,
type Foo interface {
GetName() string
GetName2() sealed
}
type sealed int
英文:
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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论