If I embed an interface with a lowercase name (private) into another interface with an uppercase name (public), I assume that code outside of the defining package cannot see the embedded private interface. Is this assumption correct?
type b interface {
G() int
}
type A interface {
F() string
b
}
Code outside of the defining package cannot "see" the embedded b
, correct? Outside code cannot call G
through an instance of A
.
Interface embedding in interface is nothing more than merging the method set of the embedded interface into the embedder, so it becomes part of the method set of the embedder type. It doesn't matter if the embedded interface type is exported or not. Quoting from Spec: Interface types:
An interface
T
may use a (possibly qualified) interface type nameE
in place of a method specification. This is called embedding interfaceE
inT
; it adds all (exported and non-exported) methods ofE
to the interfaceT
.
All that happens in your code is that A
will be an interface with 2 methods: F() string
and G() int
. There won't be a "field" like A.b
because A
is an interface type, not a struct. So this isn't particularly "interesting".
How can you try it?
Create a folder subplay
in any package, subplay.go
:
package subplay
type b interface {
G() int
}
type A interface {
F() string
b
}
Create another go
file that imports this subplay
, e.g. play.go
:
package main
import "play/subplay"
func main() {
var a subplay.A
a.G()
}
And it compiles. Runtime panic occurs though because a
is not initialized (or rather left being nil
), but if it would be, the a.G()
call would not panic.
With the following additions there will be no run-time panic:
In subplay.go
, add:
type aimpl struct{}
func (aimpl) F() string { return "aimpl.F() called" }
func (aimpl) G() int { return 1 }
func NewA() A {
return aimpl{}
}
And in play.go
:
func main() {
var a subplay.A
a = subplay.NewA()
a.G()
}
The above code compiles and runs, and does not panic.
Also note that you can create a type in another package that implements subplay.A
, you do not need to refer to subplay.b
, because all that matters is the method set. The following another
type also implements subplay.A
, you can place this in play.go
:
type another struct{}
func (another) F() string { return "aimpl.F() called" }
func (another) G() int { return 1 }
func main() {
var a subplay.A
a = another{}
a.G()
}
This again compiles and runs without run-time panic.
A similar and more interesting case would be to embed an unexported struct (or an interface) type in a struct (and not in an interface), which "truly" creates an A.b
field. Moreover, fields and methods of the embedded type get promoted to the embedder as if they would be fields or methods of the embedder. Quoting from Spec: Struct types:
A field or method
f
of an embedded field in a structx
is called promoted ifx.f
is a legal selector that denotes that field or methodf
.Promoted fields act like ordinary fields of a struct except that they cannot be used as field names in composite literals of the struct.
Code outside of the defining package cannot refer to the embedded field A.b
, this is correct, but code outside of the defining package can call the promoted A.G()
method because this identifier is not lower-cased, so the restriction does not apply to it.
The restriction for non-exported identifiers is enforced by the compiler when you attempt to refer to them. Writing a.b.G()
from another package is a compile-time error, because you are referring to a non-exported identifier (a.b
). When you write a.G()
, you are not referring to any non-exported identifiers, only to the exported a.G
identifier, so it is allowed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With