Dynamic types and dynamic values of interfaces?
You can see from the source code: iface
It contains two fields: tab
an interface table pointer, which points to type information; data
and a data pointer, which points to specific data. They are called respectively 动态类型
and 动态值
. And interface values include 动态类型
and 动态值
.
[Extension 1] nil
Compare the interface type with
A zero value for an interface value means both 动态类型
and . This interface value will be considered only if and only if the values of both parts are .动态值
nil
nil
接口值 == nil
Let’s see an example:
package main
import "fmt"
type Coder interface {
code()
}
type Gopher struct {
name string
}
func (g Gopher) code() {
fmt.Printf("%s is coding\n", g.name)
}
func main() {
var c Coder
fmt.Println(c == nil)
fmt.Printf("c: %T, %v\n", c, c)
var g *Gopher
fmt.Println(g == nil)
c = g
fmt.Println(c == nil)
fmt.Printf("c: %T, %v\n", c, c)
}
Output:
true
c: <nil>, <nil>
true
false
c: *main.Gopher, <nil>
At the beginning, c
the dynamic type and dynamic value of are both nil
, g
and nil
when is assigned g
to c
, c
the dynamic type of becomes *main.Gopher
, although c
the dynamic value of is still nil
, but when compared with , the result c
is .nil
false
[Extension 2]
Let’s take a look at an example and its output:
package main
import "fmt"
type MyError struct {}
func (i MyError) Error() string {
return "MyError"
}
func main() {
err := Process()
fmt.Println(err)
fmt.Println(err == nil)
}
func Process() error {
var err *MyError = nil
return err
}
Function execution result:
<nil>
false
Here we first define a MyError
structure, implement Error
the function, and also implement error
the interface. Process
The function returns an error
interface, which implies type conversion. So, although its value is nil
, actually its type is *MyError
, and nil
when finally compared with , the result is false
.
[Extension 3] How to print out the dynamic type and value of the interface?
Look directly at the code:
package main
import (
"unsafe"
"fmt"
)
type iface struct {
itab, data uintptr
}
func main() {
var a interface{} = nil
var b interface{} = (*int)(nil)
x := 5
var c interface{} = (*int)(&x)
ia := *(*iface)(unsafe.Pointer(&a))
ib := *(*iface)(unsafe.Pointer(&b))
ic := *(*iface)(unsafe.Pointer(&c))
fmt.Println(ia, ib, ic)
fmt.Println(*(*int)(unsafe.Pointer(ic.data)))
}
iface
A structure is directly defined in the code , using two pointers to describe itab
and data
, and then the contents of a, b, c in the memory are forced to be interpreted as our own iface
. Finally, you can print out the addresses of dynamic types and dynamic values.
The running results are as follows:
{
0 0} {
17426912 0} {
17426912 842350714568}
5
The dynamic type and address of the dynamic value of a are both 0, which is nil; the dynamic type of b is the same as the dynamic type of c; *int
finally, the dynamic value of c is 5.
The compiler automatically detects whether the type implements the interface
I often see some strange usages similar to the following in some open source libraries:
var _ io.Writer = (*myWriter)(nil)
At this time, you will be a little confused and don't know what the author wants to do. In fact, this is the answer to this question. The compiler will check *myWriter
whether the type implements io.Writer
the interface.
Let’s look at an example:
package main
import "io"
type myWriter struct {
}
/*func (w myWriter) Write(p []byte) (n int, err error) {
return
}*/
func main() {
// 检查 *myWriter 类型是否实现了 io.Writer 接口
var _ io.Writer = (*myWriter)(nil)
// 检查 myWriter 类型是否实现了 io.Writer 接口
var _ io.Writer = myWriter{}
}
After commenting out the Write function defined for myWriter, run the program:
src/main.go:14:6: cannot use (*myWriter)(nil) (type *myWriter) as type io.Writer in assignment:
*myWriter does not implement io.Writer (missing Write method)
src/main.go:15:6: cannot use myWriter literal (type myWriter) as type io.Writer in assignment:
myWriter does not implement io.Writer (missing Write method)
Error message: *myWriter/myWriter does not implement the io.Writer interface, that is, the Write method is not implemented.
After uncommenting, no error is reported when running the program.
In fact, the above assignment statement will cause implicit type conversion. During the conversion process, the compiler will detect whether the type on the right side of the equal sign implements the function specified by the interface on the left side of the equal sign.
To summarize, you can check whether the type implements the interface by adding code similar to the following:
var _ io.Writer = (*myWriter)(nil)
var _ io.Writer = myWriter{}
How to use interface to achieve polymorphism?
Go
The language is not designed with concepts such as virtual functions, pure virtual functions, inheritance, multiple inheritance, etc., but it supports object-oriented features very elegantly through interfaces.
Polymorphism is a run-time behavior that has the following characteristics:
- One type has abilities of multiple types
- Allow different objects to react flexibly to the same message
- treat each used object in a common way
- Non-dynamic languages must be implemented through inheritance and interfaces
Look at a code example that implements polymorphism:
package main
import "fmt"
func main() {
qcrao := Student{age: 18}
whatJob(&qcrao)
growUp(&qcrao)
fmt.Println(qcrao)
stefno := Programmer{age: 100}
whatJob(stefno)
growUp(stefno)
fmt.Println(stefno)
}
func whatJob(p Person) {
p.job()
}
func growUp(p Person) {
p.growUp()
}
type Person interface {
job()
growUp()
}
type Student struct {
age int
}
func (p Student) job() {
fmt.Println("I am a student.")
return
}
func (p *Student) growUp() {
p.age += 1
return
}
type Programmer struct {
age int
}
func (p Programmer) job() {
fmt.Println("I am a programmer.")
return
}
func (p Programmer) growUp() {
// 程序员老得太快 ^_^
p.age += 10
return
}
The code first defines an Person
interface, including two functions:
job()
growUp()
Then, two more structures are defined, Student
and , at the same time , Programmer
the type implements the two functions defined by the interface. Note that the type implements the interface, but the type does not.*Student
Programmer
Person
*Student
Student
After that, I defined Person
two functions whose function parameters are interfaces:
func whatJob(p Person)
func growUp(p Person)
main
In the function, the objects of Student
and are first generated Programmer
, and then they are passed into the functions whatJob
and respectively growUp
. In the function, the interface function is called directly. During actual execution, it depends on the entity type that is finally passed in, and the function implemented by the entity type is called. As a result, different objects can have multiple representations of the same message, 多态
which is achieved.
To go a little deeper, inside the function whatJob()
or growUp()
, the interface person
is bound to the entity type *Student
or Programmer
. According to the source code analyzed earlier , the function saved in iface
will be directly called here , similar to: , and because the array stores functions implemented by entity types, when the function passes in different entity types, the calls are actually different Function implementation, thereby achieving polymorphism.fun
s.tab->fun[0]
fun
Run the code:
I am a student.
{
19}
I am a programmer.
{
100}
What are the similarities and differences between Go interfaces and C++ interfaces?
An interface defines a specification that describes the behavior and functions of a class without specific implementation.
C++ interfaces are implemented using abstract classes. If at least one function in the class is declared as a pure virtual function, the class is an abstract class. Pure virtual functions are specified by using "= 0" in the declaration. For example:
class Shape
{
public:
// 纯虚函数
virtual double getArea() = 0;
private:
string name; // 名称
};
The purpose of designing an abstract class is to provide an appropriate base class for other classes to inherit from. An abstract class cannot be used to instantiate objects, it can only be used as an interface.
A derived class needs to explicitly declare that it inherits from the base class and needs to implement all pure virtual functions in the base class.
The way C++ defines interfaces is called "intrusive", while Go uses a "non-intrusive" method, which does not require explicit declaration. You only need to implement the functions defined by the interface, and the compiler will automatically recognize it.
The difference in the way C++ and Go define interfaces also leads to differences in underlying implementation. C++ uses virtual function tables to implement base classes calling functions of derived classes; while Go uses fields itab
in fun
to implement interface variables calling functions of entity types. The virtual function table in C++ is generated at compile time; while the fields itab
in Go fun
are dynamically generated during runtime. The reason is that entity types in Go may inadvertently implement N multiple interfaces. Many interfaces are not originally needed, so one cannot be generated for all interfaces implemented by the type. This is also the impact of " itab
non-intrusive"; this is This does not exist in C++ because derivation requires an explicit declaration of which base class it inherits from.