当前位置:网站首页>Go language core 36 lecture (go language advanced technology 8) -- learning notes
Go language core 36 lecture (go language advanced technology 8) -- learning notes
2022-06-24 02:36:00 【Zhengziming】
14 | Rational use of interface types
Leading content : The basics of using interfaces correctly
stay Go In the context of language , When we are talking about “ Interface ” When , It must refer to the interface type . Because the interface type is different from other data types , It cannot be instantiated .
More specifically , We can neither call new Function or make Function creates a value of an interface type , It is also impossible to express the value of an interface type by literal .
For an interface type , If no data type can be used as its implementation , Then the value of the interface cannot exist .
I've shown you before , By keyword type and interface, We can declare the interface type .
The type literal of the interface type looks somewhat similar to that of the structure type , They all wrap some core information in curly braces . It's just , The structure type wraps its field declaration , The interface type wraps its method definition .
What you should pay attention to here is : These methods in the interface type declaration represent the method collection of the interface . The set of methods of an interface is all its characteristics .
For any data type , As long as its method set completely contains all the characteristics of an interface ( That is, the whole method ), Then it must be the implementation type of this interface . Like the following :
type Pet interface {
SetName(name string)
Name() string
Category() string
}I declared an interface type Pet, It contains 3 Method definitions , Method names are SetName、Name and Category. this 3 The two methods together make up the interface type Pet The method set of .
As long as there is this in the method collection of a data type 3 A way , Then it must be Pet The implementation type of the interface . This is a non intrusive interface implementation . There is also a proper noun in this way , It's called “Duck typing”, Chinese Translation “ The duck type ”. You can go to Baidu's Encyclopedia page https://baike.baidu.com/item/%E9%B8%AD%E5%AD%90%E7%B1%BB%E5%9E%8B Go up and find out the details .
By the way , How to determine that a method of a data type implements a method of an interface type ?
There are two necessary and sufficient conditions , One is “ The signatures of the two methods need to be exactly the same ”, The other is “ The two method as like as two peas. ”. obviously , This is more rigorous than judging whether a function implements a function type .
If you refer to the last example attached to the last article , Then you will know , Although the structure type Cat No Pet The implementation type of the interface , But its pointer type *Cat It is the implementation type of this .
If you don't know why , Then please follow me . I have already put Cat Type declaration moved to demo31.go In file , And some simplifications are made , So that you can see more clearly . by the way , because Cat and Pet Their pronunciation is too similar , I also put Cat Rename to Dog.
package main
import "fmt"
type Pet interface {
SetName(name string)
Name() string
Category() string
}
type Dog struct {
name string // name .
}
func (dog *Dog) SetName(name string) {
dog.name = name
}
func (dog Dog) Name() string {
return dog.name
}
func (dog Dog) Category() string {
return "dog"
}
func main() {
// Example 1.
dog := Dog{"little pig"}
_, ok := interface{}(dog).(Pet)
fmt.Printf("Dog implements interface Pet: %v\n", ok)
_, ok = interface{}(&dog).(Pet)
fmt.Printf("*Dog implements interface Pet: %v\n", ok)
fmt.Println()
// Example 2.
var pet Pet = &dog
fmt.Printf("This pet is a %s, the name is %q.\n",
pet.Category(), pet.Name())
}Type I declare Dog Incidental 3 A way . Among them is 2 Multiple value method , Namely Name and Category, There is also a pointer method SetName.
That means ,Dog The method collection of the type itself contains only 2 A way , That is, all value methods . And its pointer type *Dog The method collection contains 3 A way ,
in other words , It has a Dog All value methods and pointer methods attached to the type . And because of this 3 The two methods are exactly Pet Implementation of a method in the interface , therefore *Dog Type becomes Pet The implementation type of the interface .
dog := Dog{"little pig"}
var pet Pet = &dogBecause of that , I can declare and initialize a Dog Variable of type dog, Then assign its pointer value to the type Pet The variable of pet.
Here are some nouns you need to remember first . For an interface type variable , For example, the variables above pet, The value we assign to it can be called its actual value ( Also called dynamic value ), The type of the value can be called the actual type of the variable ( Also known as dynamic types ).
such as , We put the address expression &dog The result value of is assigned to the variable pet, The resulting value is the variable pet The dynamic value of , The type of this result value *Dog Is the dynamic type of the variable .
The term dynamic type is relative to static type . For variables pet Speaking of , Its static type is Pet, And always Pet, But its dynamic type will change with the dynamic value we assign to it .
such as , Only I put one Dog Assign a value of type to a variable pet after , The dynamic type of this variable is Dog. If there's another one Pet The implementation type of the interface Fish, And I assigned a value of this type to pet, Then its dynamic type will become Fish.
also , Before we give an interface type variable an actual value , Its dynamic type does not exist .
You need to figure out the variables of the interface type ( Hereinafter referred to as interface variables ) The dynamic value of 、 What do dynamic and static types mean . Because I will explain deeper knowledge based on these concepts later .
Okay , I'll be right next “ How to use it well Go Language interface ” This topic raises a series of questions , Please also think about these problems with me .
So today's question is : What happens when we assign a value to an interface variable ?
In order to highlight the problem , I put Pet The declaration of the interface is simplified .
type Pet interface {
Name() string
Category() string
}I removed it Pet The name of the interface is SetName Methods . thus ,Dog The type becomes Pet The implementation type of the interface . You can demo32.go The code for this problem is found in the file .
package main
import (
"fmt"
)
type Pet interface {
Name() string
Category() string
}
type Dog struct {
name string // name .
}
func (dog *Dog) SetName(name string) {
dog.name = name
}
func (dog Dog) Name() string {
return dog.name
}
func (dog Dog) Category() string {
return "dog"
}
func main() {
// Example 1.
dog := Dog{"little pig"}
fmt.Printf("The dog's name is %q.\n", dog.Name())
var pet Pet = dog
dog.SetName("monster")
fmt.Printf("The dog's name is %q.\n", dog.Name())
fmt.Printf("This pet is a %s, the name is %q.\n",
pet.Category(), pet.Name())
fmt.Println()
// Example 2.
dog1 := Dog{"little pig"}
fmt.Printf("The name of first dog is %q.\n", dog1.Name())
dog2 := dog1
fmt.Printf("The name of second dog is %q.\n", dog2.Name())
dog1.name = "monster"
fmt.Printf("The name of first dog is %q.\n", dog1.Name())
fmt.Printf("The name of second dog is %q.\n", dog2.Name())
fmt.Println()
// Example 3.
dog = Dog{"little pig"}
fmt.Printf("The dog's name is %q.\n", dog.Name())
pet = &dog
dog.SetName("monster")
fmt.Printf("The dog's name is %q.\n", dog.Name())
fmt.Printf("This pet is a %s, the name is %q.\n",
pet.Category(), pet.Name())
}Now? , I declared and initialized a Dog Variable of type dog, Now it's name The value of the field is "little pig". then , I assigned this variable to a Pet Variable of type pet. Finally, I call dog Methods SetName It's name The value of the field has been changed to "monster".
dog := Dog{"little pig"}
var pet Pet = dog
dog.SetName("monster")therefore , The specific question I want to ask is : After the above code is executed ,pet Field of variable name What would be the value of ?
The typical answer to this question is :pet Field of variable name The value of PI is still zero "little pig".
Problem analysis
First , because dog Of SetName Methods are pointer methods , So the receiver held by this method is pointing to dog A copy of the pointer value of , Therefore, for the receiver name Field setting is the setting of variables dog The changes to the . So when dog.SetName("monster") After performing ,dog Of name The value of the field must be "monster". If you understand this layer , Then please be careful of the trap ahead .
Why? dog Of name The field value has changed , and pet But not ? Here's a general rule you need to know : If we use one variable to assign a value to another variable , So what is really given to the latter , Not the value held by the former , It is a copy of the value .
for example , I declare and initialize a Dog Variable of type dog1, Now it's name yes "little pig". then , I'm putting dog1 Assigned to a variable dog2 after , Revised dog1 Of name Value of field . At this time ,dog2 Of name What is the value of the field ?
dog1 := Dog{"little pig"}
dog2 := dog1
dog1.name = "monster"This question is almost the same as the previous one , But the interface type is not involved here . At this moment dog2 Of name Will still be "little pig". This is another embodiment of the general rule I just told you .
When you know this general rule , You can really get the previous question right . however , If you only say this reason when I ask you why , that , I can only say that you are only half right .
So what's the other half ? This needs to start with the storage mode and structure of interface type values . I said earlier , The interface type itself cannot be valued . Before we give it an actual value , Its value must be nil, This is also its zero value .
On the contrary , Once it is given a value of an implementation type , Its value is no longer nil 了 . But be careful , Even if we put it like before dog The value of is assigned to pet,pet The value of is equal to dog The values of are also different . This is not just the difference between the copy and the original value .
When we assign a value to an interface variable , The dynamic type of the variable is stored in a special data structure with its dynamic value .
Strictly speaking , The value of such a variable is actually an instance of this special data structure , Instead of the actual value we assigned to the variable . So I said ,pet The value of is equal to dog The value of must be different , Whatever is stored from them , Or the storage structure . however , We can argue that , At this time pet The value of contains dog Copy of value .
We call this special data structure iface Well , stay Go Linguistic runtime It's actually called that name in the bag .
iface An instance of will contain two pointers , One is a pointer to type information , The other is a pointer to a dynamic value . The type information here is carried by an instance of another dedicated data structure , It contains the type of dynamic value , And the methods that make it implement the interface and the ways to call them , wait .
All in all , When interface variables are given dynamic values , What is stored is a more complex value containing a copy of the dynamic value . Do you understand ?
Knowledge expansion
problem 1: When is the value of the interface variable really nil?
This problem doesn't seem to be a problem at first . For a variable of reference type , Whether its value is nil It all depends on what we give it , Is that right ? Let's start with a piece of code :
var dog1 *Dog
fmt.Println("The first dog is nil. [wrap1]")
dog2 := dog1
fmt.Println("The second dog is nil. [wrap1]")
var pet Pet = dog2
if pet == nil {
fmt.Println("The pet is nil. [wrap1]")
} else {
fmt.Println("The pet is not nil. [wrap1]")
}stay demo33.go In this code of the file , I made a statement first *Dog Variable of type dog1, And it is not initialized . What is the value of this variable ? Obviously nil. Then I assigned the variable to dog2, The value of the latter must also be nil, Am I right? ?
Now comes the question : When I put dog2 Assign to Pet Variable of type pet after , Variable pet What would be the value of ? The answer is nil Do you ?
If you really understand what I said in the analysis of the last question , Especially the data structure of interface variable assignment and its value , Then this question is not difficult to answer . You can think about it first , Then look down .
When we put dog2 The value of is assigned to the variable pet When ,dog2 The value of is copied first , But since its value here is nil, So there's no need to copy .
then ,Go The language will use the special data structure I mentioned above iface An instance of wrapping this dog2 A copy of the value of , Here is nil.
Although the wrapped dynamic value is nil, however pet The value of will not be nil, Because this dynamic value is just pet Just part of the value .
By the way , At this moment pet Dynamic types exist , yes *Dog. We can go through fmt.Printf Functions and placeholders %T To verify this , in addition reflect Bag TypeOf Functions can play a similar role .
From another perspective . We put nil Given to pet, however pet The value of is not nil.
It's strange, isn't it ? It's not . stay Go In language , We take it literally nil The value represented is called untyped nil. This is real nil, Because its type is also nil Of . although dog2 The value of is true nil, But when we assign this variable to pet When ,Go Language considers its type and value together .
in other words , At this time Go Language will recognize giving pet The value of is a *Dog Type of nil. then ,Go Language will use one iface Wrap it with an instance of , The packaged product is definitely not nil 了 .
As long as we put a type nil Assign to interface variable , Then the value of this variable must not be the real nil. therefore , When we use the judgment symbol == Judge pet Is it related to literal quantity nil On equal terms , The answer must be false.
that , How to make the value of an interface variable really nil Well ? Or just declare it without initialization , Or just take the literal amount nil Give it .
demo33
package main
import (
"fmt"
"reflect"
)
type Pet interface {
Name() string
Category() string
}
type Dog struct {
name string // name .
}
func (dog *Dog) SetName(name string) {
dog.name = name
}
func (dog Dog) Name() string {
return dog.name
}
func (dog Dog) Category() string {
return "dog"
}
func main() {
// Example 1.
var dog1 *Dog
fmt.Println("The first dog is nil.")
dog2 := dog1
fmt.Println("The second dog is nil.")
var pet Pet = dog2
if pet == nil {
fmt.Println("The pet is nil.")
} else {
fmt.Println("The pet is not nil.")
}
fmt.Printf("The type of pet is %T.\n", pet)
fmt.Printf("The type of pet is %s.\n", reflect.TypeOf(pet).String())
fmt.Printf("The type of second dog is %T.\n", dog2)
fmt.Println()
// Example 2.
wrap := func(dog *Dog) Pet {
if dog == nil {
return nil
}
return dog
}
pet = wrap(dog2)
if pet == nil {
fmt.Println("The pet is nil.")
} else {
fmt.Println("The pet is not nil.")
}
}problem 2: How to realize the combination between interfaces ?
Embedding between interface types is also called interface composition . I talked about embedded fields of struct types earlier , This actually means the embedding between structure types .
Embedding between interface types is simpler , Because it doesn't involve inter method “ shielding ”. As long as there are methods with the same name between the combined interfaces, there will be conflicts , So it doesn't compile , This is true even if the signatures of methods with the same name are different from each other . therefore , The combination of interfaces can never lead to “ shielding ” The appearance of the phenomenon .
It is similar to embedding between structure types , We just need to write the name of one interface type directly to the member list of another interface type . such as :
type Animal interface {
ScientificName() string
Category() string
}
type Pet interface {
Animal
Name() string
}Interface type Pet Contains two members , One represents another interface type Animal, One is the method Name The definition of . They are all contained in Pet In curly brackets of the type declaration , And each has its own line . here ,Animal All the methods contained in the interface become Pet Interface method .
Go The language team encourages us to declare smaller interfaces , It is suggested that we extend the program through this combination of interfaces 、 Increase the flexibility of the program .
This is because compared to a large interface with many methods , Small interfaces can be more focused on expressing a capability or a class of characteristics , It's also easier to put together .
Go Language standard library code package io Medium ReadWriteCloser Interface and ReadWriter Interface is such an example , They are all composed of several small interfaces . With io.ReadWriteCloser Interface, for example , It is from io.Reader、io.Writer and io.Closer These three interfaces are composed of .
All three interfaces contain only one method , It is a typical small interface . Each of them represents only one ability , They are readout 、 Write and close . It is usually easy for us to write the implementation types of these small interfaces . also , Once we achieve them at the same time , It is equivalent to implementing their composite interface io.ReadWriteCloser.
Even if we only achieve io.Reader and io.Writer, Then it is equivalent to the realization of io.ReadWriter Interface , Because the latter is composed of the first two interfaces . You can see , These are a few io The interfaces in the package together form an interface matrix . They are both interrelated and independent .
I am here demo34.go A small example that can reflect the advantages of interface combination is written in the file , You can check it out . All in all , Making good use of interface combinations and small interfaces can make your program framework more stable and flexible .
package main
import (
"fmt"
)
type Animal interface {
// ScientificName Used to obtain the scientific name of an animal .
ScientificName() string
// Category Used to obtain the basic classification of animals .
Category() string
}
type Named interface {
// Name Used to get the name .
Name() string
}
type Pet interface {
Animal
Named
}
type PetTag struct {
name string
owner string
}
func (pt PetTag) Name() string {
return pt.name
}
func (pt PetTag) Owner() string {
return pt.owner
}
type Dog struct {
PetTag
scientificName string
}
func (dog Dog) ScientificName() string {
return dog.scientificName
}
func (dog Dog) Category() string {
return "dog"
}
func main() {
petTag := PetTag{name: "little pig"}
_, ok := interface{}(petTag).(Named)
fmt.Printf("PetTag implements interface Named: %v\n", ok)
dog := Dog{
PetTag: petTag,
scientificName: "Labrador Retriever",
}
_, ok = interface{}(dog).(Animal)
fmt.Printf("Dog implements interface Animal: %v\n", ok)
_, ok = interface{}(dog).(Named)
fmt.Printf("Dog implements interface Named: %v\n", ok)
_, ok = interface{}(dog).(Pet)
fmt.Printf("Dog implements interface Pet: %v\n", ok)
}summary
Go Language interfaces are often used to represent certain capabilities or characteristics . First , What we need to make clear is , Dynamic values of interface variables 、 What both dynamic and static types represent . These are the basis for the correct use of interface variables . When we assign a value to an interface variable , The interface variable holds a copy of the assigned value , Not itself .
what's more , The value of the interface variable is not equivalent to this copy that can be called a dynamic value . It will contain two pointers , A pointer to a dynamic value , A pointer to type information .
Based on this , Even if we take a value as nil A variable of an implementation type of is assigned to the interface variable , The latter value cannot be true nil. Although its dynamic value will be nil, But its dynamic type does exist .
please remember , Unless we only declare and do not initialize , Or explicitly assign it nil, Otherwise, the value of the interface variable will not be zero nil.
The latter question is relatively easy , It is about programming . It's always good to use small interfaces and interface combinations , We can form the interface matrix , Then set up a flexible program framework . If you use the embedding method between structure types when implementing the interface , Then the combination of interfaces can play a greater role .
Thinking questions
If we take a value as nil A variable of an implementation type of is assigned to the interface variable , Can the method of the interface still be called on this interface variable ? If possible , What are the precautions ? If not , What's the reason ?
边栏推荐
- How many graphics cards are required for cloud game servers? What should be paid attention to when purchasing servers
- How about Shenzhen website construction? Is it expensive?
- Can cloud computing scale flexibly? What are the characteristics of elasticity?
- Efficient Internet access and systematic learning
- [Tencent cloud double 12 audio and video communication special session] from 9 yuan for Q4 counter attack artifact, SMS and security (New) package!
- If you accidentally make the disk dynamic, how to convert it back (do not guarantee it, but take a snapshot before operation)
- What is the difference between trademark registration and company domain name? What is the difference between the two?
- Flink practice tutorial: getting started 1- zero basic users realize simple Flink tasks
- [expense center] demand & problem feedback week is coming! Feedback wins a good gift!
- Tencent cloud temporary secret key scheme - character recognition example
猜你喜欢
随机推荐
How about Shenzhen website construction? Is it expensive?
The same set of code returns normally sometimes and reports an error sometimes. Signature error authfailure SignatureFailure
How to formulate a domain name trademark registration scheme? What if the plan is rejected?
November 1 global network security hotspot
Data backup is required for manual upgrade of WordPress website program
Is a trademark domain name useful? How long does it take to register a domain name?
Vivo global mall: design and practice of commodity system architecture
[tcapulusdb knowledge base] manually view the online operation of tcapulusdb
How to access easynvr management platform through web pages without data?
Pod abnormal troubleshooting
The dealer management and control platform in the leather industry simplifies the purchase approval process and easily controls agents
Leetcode problem solving notes for slow ploughing of stupid cattle (dynamic update...)
Must the company domain name have a trademark registration? What if the registered domain name is rejected?
What is a port? The most complete and strongest port number in history, collection!
How to explain to a 10-year-old how information is transmitted through the air? Contains a lot of network knowledge!
A detailed explanation of the laser slam framework logo-loam
Cloud rendering: cloud exhibition hall of Tencent digital ecology Conference - open roaming mode on cloud
Frequent screen flashing after VNC login - abnormal system time
How to build a website? What needs attention?
Using robot framework to realize multi platform automated testing


