In C/C++ we can use generic methods to make the code reusable, most common, such as stl functions: vector<int> vint or vector<float> vfloat, etc. This article will use the interface{...} interface to enable Golang to implement generics.
interface{...} is the basis for implementing generics. If an array element type is interface{...}, then all entities that implement the interface can be placed into the array. Note that it does not necessarily have to be an empty interface (we can implement the interface by converting it into a custom type). Why do we need to declare methods in interface: Because when we need to operate on data in arrays (such as comparing sizes), we need to declare a custom method for this operation. In other words, only entities that implement this method are allowed to be added to the array.
Basic Demo
In the demo demonstrated below, we will implement a simplest vector and implement the function of sorting when inserting.
type Comper interface{ Lessthan (Comper) bool } type Sdata struct{ data []Comper } func (t *Sdata) Push (item Comper){ = append(, item) for k,v:=range { if (v) { //Calling the interface defined method //Sorting operation break } } }
This implements the simplest demo. The array elements using Sdata must first implement the Lessthan method:
type Myint int func (t Myint) Lessthan (x Comper) bool { return t<x.(Myint) } func main() { mydata := Sdata{make([]Comper, 0)} for i:=10;i>0;i--{ ((Myint(i))) } (mydata) }
However, this demo also has many disadvantages. First, simple type elements cannot be sorted using Sdata, and second, concurrency does not support, which will produce unpredictable results in the case of concurrency.
Support simple types of demos via Reflect
To support simple types, we can only use empty interfaces as array element types. At this time, our program logic should be like this: If this is a simple type, then we directly call the built-in "<" and ">" to compare; if this is not a simple type, then we still call the Lessthan method:
type Comper interface{ Lessthan (Comper) bool } type Sdata struct{ data []interface{} } func (t *Sdata) Push (item interface{}){ for _,v:=range { if (item).Implements((new(Comper)).Elem()) { citem:=item.(Comper) cv:=v.(Comper) if (cv) { //Operation to be performed break } }else{ x,v:=(item),(v) switch () { case : case reflect.Int8: case reflect.Int16: /*...*/ //x, y:=(), () /*...*/ break case : /*...*/ } } } }
Use reflect to determine the type of item:
(item).Implements((new(comper)).Elem()), that is, whether the item type implements the comper interface type. TypeOf(new(compper)) is a pointer ptr, Elem() converts the pointer to a value. If the return value of this function is true, you can force item and v from interface{} to Comper interface and call Lesstthan(...); of course you can also use type assertions, which is simpler and more commonly used. I'm just trying to use reflection here: if v, ok:=item.(comper); ok{...}
You cannot directly compare the size of the value type:
The value type cannot be directly compared with ">" and "<" even if we know it is a simple type. The author has not found a simple method to directly convert values to simple types and compare them, so he adopted the enumeration method. If there is an easier way, please let me know.
If using instance pointers to implement the interface:
This is a relatively difficult problem to discover, involving golang's type system. That is, if our method to implement Lessthen is like this func (t*Myint) Lessthan (x Compper) bool, then it is very likely that your assertion item type will fail. We can look at the type of item at this time:
(([0])) //
This is not what we expect, because we know that only the method set of *T type is S and *S, while the method set of T type is S. Obviously, the Lesstthan method is not included in the method set, only * is included. So the correct way to use it is to assign the value to the pointer type when the value is initially assigned:
mi := Myint(i) (&mi)
Multi-interface layered demo
An empty interface is actually just a special use case. After we promote it, we can find that we can define multiple interfaces and declare multiple methods. The entity has the permission to call several functions after implementing several methods:
For example, we can give read permissions, write permissions and delete permissions to correspond to different needs:
type Reader interface { Read () interface{} } type Writer interface { Write (Writer) } type ReadWriter interface { Reader Writer } type Remover interface { Remove () } type Sdata struct { data []interface{} } func (t *Sdata)Get(i int)interface{}{ if len() == 0{return nil} if ([0]).Implements((new(Reader)).Elem()) == true{ return [i].(Reader).Read() } } func (t *Sdata)Modify(i int, w Writer){ // if ([0]).Implements((new(ReadWriter)).Elem()) == true if _,ok:=[0].(ReadWriter);ok{ [i].(Writer).Write(w) } } //......
Customize Myint type and implement Reader and Writer interface:
type Readint int func (t Readint) Read() interface{}{ return int(t) } //--------------------------------------------- type Myint int func (t Myint) Read() interface{}{ return int(t) } func (t *Myint) Write(w Writer){ *t = *w.(*Myint) return } func main() { mydata := Sdata{make([]interface{}, 1)} var u,v Myint = 5,6 [0] = &u ("Myint is ", (0)) (0,&v) ("Myint is ", (0)) var ru Readint = 100 readdata := Sdata{make([]interface{}, 1)} [0] = &ru ("Readint is ", (0)) //var rv Readint = 101 (0,&v) //In fact, if you pass rv, the compilation will not pass at all. ("Readint is ", (0)) }
Running results:
Myint is 5
Myint is 6
Readint is 100
Readint is 100
Note: It is not advisable if you do not perform type checking because you believe that the above code is passed &rv, which does not pass the compilation at all. Because for the empty interface interface{}, it doesn't matter the type of the entity, it only cares whether the method is implemented, so it is reasonable to pass &v. In addition, since the demo is a simple version, the judgment permission part only depends on the permissions of the 0th element. In fact, the judgment permission should be completed at initialization and stored in the structure variable.
Finally, regarding the concurrency issue, just apply a read and write lock. Too simple, no longer pass demo verification.
The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.