Interface
The most exquisite design in go language should be counted interface, it lets object-oriented, content organization realizes very convenient, when you read this chapter, you will be interface The ingenious design of admiration.
What is interface
Simply put, interface is a combination of method, and we define a set of behaviors of an object by interface.
The last example of our previous chapter student and employee can sayhi, although their internal implementation is different, but that is not important, it is important that they can say hi
Let's continue to do more extensions, student and employee implement another method sing, and then student implement the method Borrowmoney and employee implementation spendsalary.
This student implements three methods: Sayhi, Sing, Borrowmoney, and employee realizes Sayhi, Sing, Spendsalary.
The combination of these methods above is called interface (implemented by object student and employee). For example, student and employee both implement Interface:sayhi and sing, which means that the interface type is the two objects. And the employee did not realize this interface:sayhi, sing and Borrowmoney, because the employee did not realize Borrowmoney this method.
Interface type
The interface type defines a set of methods that are implemented by an object if it implements all the methods of an interface. The detailed syntax references the following example
Copy Code code as follows:
Type Human struct {
Name string
Age int
Phone string
}
Type Student struct {
Human//anonymous field Human
School string
Loan float32
}
Type Employee struct {
Human//anonymous field Human
Company string
Money float32
}
Human object Implementation Sayhi method
Func (H *human) Sayhi () {
Fmt. Printf ("Hi, I am%s" can call me on%s\n ", H.name, H.phone)
}
Human object Implementation Sing method
Func (H *human) Sing (lyrics string) {
Fmt. Println ("La La, la la los la, la los la la la la ...", lyrics)
}
Human object Implementation Guzzle method
Func (H *human) guzzle (Beerstein string) {
Fmt. Println ("Guzzle guzzle guzzle ...", Beerstein)
}
Sayhi method of employee overload human
Func (e *employee) Sayhi () {
Fmt. Printf ("Hi, I am%s, I work at%s", called me on%s\n ", E.name,
E.company, E.phone)//This sentence can be divided into multiple lines
}
Student Realization Borrowmoney Method
Func (S *student) Borrowmoney (amount float32) {
S.loan + = amount//(again and again and ...)
}
Employee Implementation Spendsalary Method
Func (e *employee) spendsalary (amount float32) {
E.money-= amount/VODKA please!!! Get me through the day!
}
Define Interface
Type Men Interface {
Sayhi ()
Sing (Lyrics string)
Guzzle (Beerstein string)
}
Type Youngchap Interface {
Sayhi ()
Sing (song string)
Borrowmoney (Amount float32)
}
Type Elderlygent Interface {
Sayhi ()
Sing (song string)
Spendsalary (Amount float32)
}
We can see from the above code that interface can be implemented by any object. We see the above men interface be implemented by human, student and employee. Similarly, an object can implement any number of interface, such as the student above, which implements the men and Youngchap two interface.
Finally, any type implements an empty interface (we define: interface{}), which is a interface that contains 0 method.
Interface value
So what is the value of interface inside? If we define a interface variable, then this variable can contain any type of object that implements the interface. For example, in the above example, we define a men interface type of variable m, then m can be stored human, student or employee values.
Since M can hold these three types of objects, we can define a slice containing men type elements that can be given to objects that implement arbitrary structures of the men interface, which is different from the slice above our traditional meaning.
Let's take a look at the following example:
Copy Code code as follows:
Package Main
Import "FMT"
Type Human struct {
Name string
Age int
Phone string
}
Type Student struct {
Human//anonymous fields
School string
Loan float32
}
Type Employee struct {
Human//anonymous fields
Company string
Money float32
}
Human realization Sayhi method
Func (H Human) Sayhi () {
Fmt. Printf ("Hi, I am%s" can call me on%s\n ", H.name, H.phone)
}
Human realization Sing method
Func (H Human) Sing (lyrics string) {
Fmt. Println ("La la la ...", lyrics)
}
Sayhi method of employee overload human
Func (e Employee) Sayhi () {
Fmt. Printf ("Hi, I am%s, I work at%s", called me on%s\n ", E.name,
E.company, E.phone)
}
Interface men is implemented by Human,student and employee
Because these three types all implement these two methods
Type Men Interface {
Sayhi ()
Sing (Lyrics string)
}
Func Main () {
Mike: = student{human{"Mike", "222-222-xxx"}, "MIT", 0.00}
PAUL: = student{human{"Paul", "111-222-xxx"}, "Harvard", 100}
Sam: = employee{human{"Sam", "444-222-xxx"}, "Golang Inc", 1000}
Tom: = employee{human{"Tom", Panax Notoginseng, "222-444-xxx"}, "Things Ltd.", 5000}
Define variable I of men type
var i Men
I can store student
i = Mike
Fmt. Println ("This is Mike, a Student:")
I.sayhi ()
I.sing ("November Rain")
I can also store employee
i = Tom
Fmt. Println ("This is Tom, Employee:")
I.sayhi ()
I.sing ("Born to Be Wild")
Defines the slice Men
Fmt. PRINTLN ("Let's" use a slice of Men and the What happens ")
x: = Make ([]men, 3)
These three are all different types of elements, but they implement interface the same interface
X[0], x[1], x[2] = Paul, Sam, Mike
For _, Value: = Range x{
Value. Sayhi ()
}
}
Through the code above, you'll find that interface is a collection of abstract methods that must be implemented by other interface types, not self-realization, and go through interface duck-typing: "When you see a bird walking like a duck, The bird can be called a duck if it swims like a duck and barks like a duck.
Empty interface
Empty interface (interface{}) does not contain any method, and because of this, all types implement an empty interface. Empty interface does not work for a description (because it does not contain any method), but an empty interface is useful when we need to store any type of numeric value, because it can store any type of numeric value. It is somewhat similar to the void* type of C language.
Copy Code code as follows:
Define a as an empty interface
var a interface{}
var i int = 5
S: = "Hello World"
A can store values of any type
A = I
A = s
A function takes interface{} as an argument, then he can accept any type of value as an argument, and if a function returns interface{}, then it can return any type of value. is not very useful ah!
interface function Parameters
Interface variables can hold arbitrary objects that implement the interface type, which gives us some extra thought about writing functions (including method), and whether we can allow functions to accept various types of arguments by defining interface parameters.
Give an example: FMT. Println is a commonly used function, but have you noticed that it can accept any type of data. Open the FMT source file and you'll see a definition like this:
Copy Code code as follows:
Type Stringer Interface {
String () string
}
That is, any type that implements the string method can be called fmt.println as a parameter, let's try
Copy Code code as follows:
Package Main
Import (
"FMT"
"StrConv"
)
Type Human struct {
Name string
Age int
Phone string
}
Through this method Human realizes the FMT. Stringer
Func (H Human) string () string {
Return "❰" +h.name+ "-" +strconv. Itoa (H.age) + "Years-✆" +h.phone+ "❱"
}
Func Main () {
Bob: = human{"Bob", "000-7777-xxx"}
Fmt. Println ("This Human is:", Bob)
}
Now let's look at the previous box example and you'll find that the color structure also defines a method:string. In fact, this is also achieved FMT. Stringer this interface, that is, if a type is required to be FMT in a special format, you must implement the Stringer interface. If this interface is not implemented, FMT will output in the default manner.
Copy Code code as follows:
Achieve the same function
Fmt. Println ("The biggest one is", boxes.) Biggestscolor (). String ())
Fmt. Println ("The biggest one is", boxes.) Biggestscolor ())
Note: An object that implements the error interface (that is, an object that implements the error () string), the error () method is invoked when the FMT output is used, so no more need to define the string () method.
Types of interface variable storage
We know that interface variables can store values of any type (the type implements interface). So how do we know in reverse what type of object is actually saved in this variable? There are two methods that are commonly used at present:
Comma-ok Assertion
The go language has a syntax that directly determines whether it is a variable of that type: value, OK = element. (t), where value is the value of the variable, OK is a bool type, element is a interface variable and T is the type of assertion.
If the value of type T is actually stored in the element, then OK returns true, otherwise it returns false.
Let's take an example to get a deeper understanding.
Copy Code code as follows:
Package Main
Import (
"FMT"
"StrConv"
)
Type Element interface{}
Type List [] Element
Type person struct {
Name string
Age int
}
The string method is defined, and the FMT is implemented. Stringer
Func (P person) string () string {
Return "(Name: + P.name +)-Age:" +strconv. Itoa (P.age) + "Years")
}
Func Main () {
List: = Make (list, 3)
LIST[0] = 1//an int
LIST[1] = "Hello"//A String
LIST[2] = person{"Dennis", 70}
For index, element: = Range List {
If value, OK: = element. (int); OK {
Fmt. Printf ("list[%d] is a int and its value is%d\n", index, value)
else if value, OK: = element. (string); OK {
Fmt. Printf ("list[%d] is a string and it value is%s\n", index, value)
else if value, OK: = element. (person); OK {
Fmt. Printf ("list[%d] is a person and its value is%s\n", index, value)
} else {
Fmt. Println ("list[%d] is of a different type", index)
}
}
}
Is it very simple, and have you noticed more than one if inside, remember when I described the process before, if it allows initialization of variables.
You may have noticed that the more types we assert, the more if else there is, and that's why the switch we're going to introduce below.
Switch test
The best explanation is the code example, now let's rewrite the implementation above
Copy Code code as follows:
Package Main
Import (
"FMT"
"StrConv"
)
Type Element interface{}
Type List [] Element
Type person struct {
Name string
Age int
}
Print
Func (P person) string () string {
Return "(Name: + P.name +)-Age:" +strconv. Itoa (P.age) + "Years")
}
Func Main () {
List: = Make (list, 3)
List[0] = 1//an int
LIST[1] = "Hello"//a string
LIST[2] = person{"Dennis", 70}
For index, element: = Range list{
Switch value: = element. (type) {
Case INT:
Fmt. Printf ("list[%d] is a int and its value is%d\n", index, value)
Case string:
Fmt. Printf ("list[%d] is a string and it value is%s\n", index, value)
Case Person:
Fmt. Printf ("list[%d] is a person and its value is%s\n", index, value)
Default
Fmt. Println ("list[%d] is of a different type", index)
}
}
}
Here's one thing to emphasize: element. (type) syntax cannot be used in any logic outside of a switch, if you want to judge a type outside the switch use COMMA-OK.
Embedding interface
What really appeals to the GO is its built-in logic syntax, like the anonymous fields we learn when we learn struct, how elegant, and the same logic introduced into interface, which is not even more perfect. If a interface1 is used as an embedded field for Interface2, then Interface2 implicitly contains the method inside Interface1.
We can see that there's a definition in the source package Container/heap
Copy Code code as follows:
Type Interface Interface {
Sort. Interface//inline field sort. Interface
Push (x interface{})//a push method to push elements into the heap
Pop () interface{}//a pop elements that pops elements from the heap
}
We see sort. Interface is actually an embedded field that puts the sort. All method of interface is included in the implicit inclusion. This is the following three methods:
Copy Code code as follows:
Type Interface Interface {
Len is the number of elements in the collection.
Len () int
Less returns whether the element with index I should sort
Before the element with index J.
Less (i, J int) bool
Swap swaps the elements with indexes I and J.
Swap (i, J int)
}
Another example is IO under the IO package. Readwriter, which includes the reader and writer two interface below the IO package:
Copy Code code as follows:
Io. Readwriter
Type Readwriter Interface {
Reader
Writer
}
Reflection
The go language enables reflection, which is the ability to check the state of a program at run time. The package we use generally is reflect package. How to use reflect bag, the official this article explained the reflect package in detail the realization principle, laws of reflection
Use reflect is generally divided into three steps, the following briefly explained: To go to reflection is a type of value (these values are implemented empty interface), first need to convert it into reflect objects (reflect. Type or reflect.value, calling a different function depending on the situation. These two methods are obtained as follows:
Copy Code code as follows:
T: = reflect. TypeOf (i)//get the type of metadata, through t we can get all the elements inside the type definition
V: = reflect. valueof (i)//get the actual value, through v we get the value stored in it, we can also change the value
After converting to a reflect object, we can do something, which is to convert the reflect object to the corresponding value, such as
Copy Code code as follows:
Tag: = T.elem (). Field (0). Tag//Get the label defined in the struct
Name: = V.elem (). Field (0). String ()//Get the value stored in the first field
Gets the reflection value to return the corresponding type and numeric value
Copy Code code as follows:
var x float64 = 3.4
V: = reflect. valueof (x)
Fmt. Println ("type:", V.type ())
Fmt. Println ("Kind is float64:", v.kind () = reflect. Float64)
Fmt. Println ("Value:", V.float ())
Finally, the reflection of the words, then the reflected field must be modifiable, we learned before the transfer value and reference, this is the same reason. The reflected field must be read-write to mean that if the following is written, an error occurs
Copy Code code as follows:
var x float64 = 3.4
V: = reflect. valueof (x)
V.setfloat (7.1)
If you want to modify the corresponding value, you must write this
Copy Code code as follows:
var x float64 = 3.4
P: = reflect. valueof (&x)
V: = P.elem ()
V.setfloat (7.1)
It's just a simple introduction to reflection, and a deeper understanding of it requires constant practice in programming.