Why does Go allow a struct to implement an unexported interface present in a different package?

huangapple go评论92阅读模式
英文:

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

huangapple
  • 本文由 发表于 2022年2月22日 01:37:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/71210680.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定