Go is a compact language designed by nature for server programs, so the design principles of Go focus on extensibility, readability, and concurrency, while polymorphism is not what the language was designed for and is therefore put aside. Although there is no generics support prior to version 2.0, Go comes with language features that satisfy some “generic-like” requirements, such as built-in types:

  1. array

  2. slice

  3. map

  4. chan

These four types can be initialized with any type of element, for example map[YourType]bool can be used to implement any set of elements. Some of Go’s built-in functions are also generic, such as len(), which can be used for string, array, slice, and map lengths.

However, if golang’s built-in constructs and functions don’t meet your needs, there are several ways to start:

Types of assertions

When you want to implement a general-purpose function, consider whether the arguments passed in are of fixed type. Go provides interface{}, which can represent any type. Use it when you are not sure what type is appropriate. Here’s a simple example:

typeContainer struct { elem []interface{} } func (this *Container) Put(v interface{}) { *this = append(*this, Elem)} / / remove the last element func (this * Container) Get () interface {} {ret: = (* c) [len (* c) - 1) * c = (* c) [: len (* c) - 1)return ret
}

func assertExample() {
    container := &Container{}
    container.Put(1)
    container.Put("Hello") _, ok := container.Get().(int); ! ok { fmt.Println("Unable to read an int from container")}}Copy the code

We hide the details completely with the interface type, but we also leave the risk of run-time type checking failure to the caller, who writes every time

_, ok := YourType.(type); ! ok{}Copy the code

This type of assertion is verbose.

Reflection mechanism

Reflection is a mechanism that dynamically calls the methods and properties of an object at runtime. It is implemented in both Python and Java, and Golang is implemented through the Reflect package. Reflection allows the program to handle objects of unknown type at compile time. This sounds like a solution to our problem above. Now modify the code:

type Container struct {
    elem reflect.Value
}
func NewContainer(t reflect.Type) *Container {
    return &Container{
        elem: reflect.MakeSlice(reflect.SliceOf(t), 0, 10),
    }
}
func (this *Container) Put(v interface{}) {
    ifreflect.ValueOf(val).Type() ! = c.elem.Type().Elem() {
        panic(fmt.Sprintf("Cannot set a %T into a slice of %s", val, c.elem.Type().Elem()))
    }
    c.elem = reflect.Append(c.elem, reflect.ValueOf(val))
}
func (this *Container) Get(regref interface{}) interface{} {
    retref = c.elem.Index(c.elem.Len()-1)
    c.elem = c.elem.Slice(0, c.elem.Len()-1)
    return retref
}
func assertExample() {a := 0.123456 nc := NewContainer(reflect.typeof (a)) nc.put (a) nc.put (1.11) nc.put (2.22) b := 0.0 c := nc.get (&b) fmt.Println(c) d := nc.Get(&b) fmt.Println(d) }Copy the code

As you can see, there is a lot more code to use reflection and to prefix the various reflect methods. This can be a disaster for code cleanliness freaks, and it can slow programmers down because there is no compile-time type checking, which incurs extra runtime overhead.

Using an interface

The sort package in the library, for example, uses the interface to sort arbitrary container elements:

type Interface interface {
    Len() int
    Less(i, j int) bool
    Swap(i, j int)
}Copy the code

As long as you implement the three methods defined by the interface, you can sort your custom container elements, refer to the documentation in the Sort package for examples. A review of the source code for the Sort package shows that the code is very concise, but the downside is obvious: the user needs to re-implement the interface methods themselves.

Code generation tool

The principle of code generation is to write a mock type that is only used as a placeholder, and then use the conversion tool to replace the placeholder with a concrete type. Many people have written this, but I won’t go into more details here. The disadvantages of this approach are that the resulting files are large and rely on third-party tools and template syntax.

Anyhow, Golang generic implementation without a fixed method, or a universal method of ideal, the program design after reaching a certain size, always need to in the code efficiency, the compiler make some trade-off between efficiency and operation efficiency, so we need to know the difference between implement generic methods, at the right time with the right that is ok.


Golang 1. X version of generic programming