The 12th day of 21 days from Java to Go - Constant dropping wears the stone (interface and reflection)

The meaning of reflection

  • For a type variable, it has two meanings: one is that the type is a declaration, and the other is what its stored value is. The type determines the storage method of variables, supported operation sets and method sets. The of values is nothing more than reading and writing. Values are stored in the format of 0 and 1 in memory. The specific interpretation of 0 and 1 needs type support. Types and values are not isolated. Go language provides reflection function to support programs to dynamically access the types and values of variables.
  • The foundation of Go language reflection is to save the compiler and runtime type information in the executable program with appropriate data structure. The reflect standard library provided by Go language only provides a set of access interfaces for language users.
  • Interfaces have static and dynamic types. Static types are defined when an interface is defined. The static type of an interface is essentially a collection of method signatures of the interface. A dynamic type is the type of a specific instance of an interface binding. Interfaces can bind different instances, so dynamic types change with different types of instances they bind.
  • Go language provides type assertion and interface type query to confirm what the initialized interface variable points to the specific type of the instance. Type assertion and interface type query are just different in syntax format and have the same semantics. Interface assertion:
o := i.(TypeName)   
if o,ok := i.(TypeName){

}

Interface type query:

switch v:= i.(TypeName){
  case type1:
    xxx
  case type2:
    xxx
  default:
    xxx
}

Purpose of Go language null interface

  • The GO language has no generics. If a function can receive any type, it uses an empty interface type to make up for the lack of generics.
  • Reflection: the empty interface is the basis of reflection implementation. The reflection library is to convert the relevant specific types and assign them to the empty interface before processing.
  • Comparison of null interfaces: null interfaces have two fields, one is the instance type, and the other is the pointer to the bound instance. Only when both are nil, the null interface is nil.

Data structure of interface

  • Interface variables must be initialized to make sense. The default value of uninitialized interface variables is nil, which has no meaning. Passing a specific type instance to an interface is called interface instantiation. During the instantiation of the interface, compile
    The processor describes this process through a specific data structure.

Data structure of non empty interface

  • The data structure is iface, which is defined in src/runtime/runtime2.go
type iface struct {
	tab  *itab
	data unsafe.Pointer
}
  • itab: store the interface's own type, bound instance type and its instance related function pointers.
  • Data pointer data: refers to the instance copy of the interface binding. The initialization of the interface is also a value copy. Data points to a copy of the instance. If the pointer type is passed to the interface, data points to a copy of the pointer. In a word, Go follows the same rule - value passing, regardless of interface conversion or function call.
type itab struct {
	inter *interfacetype  //Static type of the interface itself
	_type *_type //Store specific instance types (dynamic types)
	hash  uint32 // copy of _type.hash. Used for type switches.
	_     [4]byte
	fun   [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter.
}
  • The data structure of itab is the basis for the dynamic call of non empty interface. The information of itab is saved by the compiler and connector and stored in the read-only storage section. rodata of the executable file. In the statically allocated storage space stored in itab, it is not limited by GC, and its memory will not be recycled.
  • Go language is a strongly typed language, and the compiler will do strict type verification when compiling. Therefore, go must maintain one type of meta information for each type. This meta information will be used in both running and reflection. The general structure of the type meta information of go language is_ type(src/runtime/type.go). Other types are based on_ Type is a structure encapsulated by embedded fields.
// Needs to be in sync with ../cmd/link/internal/ld/decodesym.go:/^func.commonsize,
// ../cmd/compile/internal/reflectdata/reflect.go:/^func.dcommontype and
// ../reflect/type.go:/^type.rtype.
// ../internal/reflectlite/type.go:/^type.rtype.
type _type struct {
	size       uintptr
	ptrdata    uintptr // size of memory prefix holding all pointers
	hash       uint32
	tflag      tflag
	align      uint8
	fieldAlign uint8
	kind       uint8
	// function for comparing objects of this type
	// (ptr to object A, ptr to object B) -> ==?
	equal func(unsafe.Pointer, unsafe.Pointer) bool
	// gcdata stores the GC type data for the garbage collector.
	// If the KindGCProg bit is set in kind, gcdata is a GC program.
	// Otherwise it is a ptrmask bitmap. See mbitmap.go for details.
	gcdata    *byte
	str       nameOff
	ptrToThis typeOff
}
  • The dynamic calling process of an interface has two parts of redundant time consumption: one is the process of interface instantiation, that is, the process of establishing iface structure. Once instantiated, the interface and specific types of itab data structures can be reused. The other is method call, which is an indirect call of function pointer. Successive call is a dynamic jump call after calculation.

Data structure of empty interface

type eface struct {
	_type *_type
	data  unsafe.Pointer
}
- An empty interface is an interface without any method set, so the data structure related to dynamic memory allocation needs to be maintained inside the empty interface. The empty interface only cares about the specific type of storage. An empty interface retains a copy of the type and value of a specific instance.

Reflective essence

  • Go's reflection is based on the interface and type system. Go reflection skillfully uses the data structure used for instance to interface conversion. First, it passes the instance to the internal empty interface. In fact, it converts an instance type into a data structure eface that can be expressed by the interface, and reflects the values and types of access and operation instances based on the converted data structure.
  • The reflection package has a general structure rtype that describes the public information of types. This is the same thing as the type in the runtime during the internal implementation of the interface, but it is defined separately because of the isolation of the package. It is the general information describing types, and encapsulates a specific structure for each basic type.
package main

import (
	"fmt"
	"reflect"
)

type humnan struct {
	age int
	name string
}

func main() {
	s:=[]int{1,2,3}
	kind := reflect.TypeOf(s).Kind()
	fmt.Println(reflect.TypeOf(s))  //[]int
	fmt.Println(reflect.ValueOf(s)) //[1 2 3]
	fmt.Println(kind) //slice
        fmt.Println(reflect.TypeOf(s).Elem()) //int

	s1:="hello"
	fmt.Println(reflect.TypeOf(s1))  //string
	fmt.Println(reflect.ValueOf(s1)) //hello
	fmt.Println(reflect.TypeOf(s1).Kind()) //string

	h := humnan{name: "xiaomi", age: 10}
	fmt.Println(reflect.TypeOf(h))  //string
	fmt.Println(reflect.ValueOf(h)) //hello
	fmt.Println(reflect.TypeOf(h).Kind()) //string
}

  • For reflect.TypeOf(a) (there are two types of arguments passed in, one is an interface variable and the other is a concrete class. If it is a concrete type variable, reflect.TypeOf() It returns specific type information: if it is an interface variable, the return result of the function can be divided into two situations: if a specific type instance is bound, it returns the dynamic type of the interface, that is, the information of the bound specific instance type; if no specific type instance is bound, it returns the static type information of the interface itself.

Foundation type

  • The Type interface has a Kind() method, which returns an integer enumeration value. Different values represent different types. The Type here is an abstract concept, which is temporarily called "basic Type" For example, all structures are reduced to a basic Type struct, and all functions are reduced to a basic Type func. Basic types are divided according to the internal data structures of compiler and runtime built types. Different basic types have different final internal data structures. Thousands of specific types can be defined, and struct alone can be used To define many new types, but it comes down to a basic Type, struct. Basic types are abstract and their types are limited. A total of 26 basic types are defined.
type Kind uint

const (
	Invalid Kind = iota
	Bool
	Int
	Int8
	Int16
	Int32
	Int64
	Uint
	Uint8
	Uint16
	Uint32
	Uint64
	Uintptr
	Float32
	Float64
	Complex64
	Complex128
	Array
	Chan
	Func
	Interface
	Map
	Ptr
	Slice
	String
	Struct
	UnsafePointer
)

Added by strangermaster on Fri, 03 Dec 2021 10:33:03 +0200