A Brief introduction to Go structure struct interface Interface Reflection

  • 2020-06-03 06:53:32
  • OfStack

Structure struct

struct USES custom complex data structures that can contain multiple fields (properties) and can be nested.

The struct type in go is understood as a class that defines methods, slightly different from function definitions;

The struct type is a value type.

struct definition


type User struct {
 Name string
 Age int32
 mess string
}

var user User
var user1 *User = &User{}
var user2 *User = new(User)

struct use

In the following example, user1 and user2 are pointer types. When accessed, the compiler automatically converts user1.Name to (*user1).Name


func main() {
 var user User
 user.Name = "nick"
 user.Age = 18
 user.mess = "lover"

 var user1 *User = &User{
  Name: "dawn",
  Age: 21,
 }
 fmt.Println(*user1)     //{dawn 21 }
 fmt.Println(user1.Name, (*user1).Name) //dawn dawn

 var user2 *User = new(User)
 user2.Name = "suoning"
 user2.Age = 18
 fmt.Println(user2)      //&{suoning 18 }
 fmt.Println(user2.Name, (*user2).Name) //suoning suoning
}

The constructor

struct in golang does not have a constructor, and one can be forged


type User struct {
 Name string
 Age int32
 mess string
}

func NewUser(name string, age int32, mess string) *User {
 return &User{Name:name,Age:age,mess:mess}
}

func main() {
 //user := new(User)
 user := NewUser("suoning", 18, "lover")
 fmt.Println(user, user.mess, user.Name, user.Age)
}

Memory layout

All fields in struct are contiguous in memory


var user User
 user.Name = "nick"
 user.Age = 18
 user.mess = "lover"

 fmt.Println(user)     //{nick 18 lover}
 fmt.Printf("Name:%p\n", &user.Name) //Name:0xc420016180
 fmt.Printf("Age: %p\n", &user.Age) //Age: 0xc420016190
 fmt.Printf("mess:%p\n", &user.mess) //mess:0xc420016198 8 Bytes align memory 

methods

Methods are applied to variables of a particular type, so custom types can have methods, not just struct.

Method access control is also case-controlled.

The init function is implemented by passing in a pointer, which changes the value of the struct field because it is a value type.


type User struct {
 Name string
 Age int
 sex string
}

func (this *User) init(name string, age int, sex string) {
 this.Name = name
 this.Age = age
 this.sex = sex
}

func (this User) GetName() string {
 return this.Name
}

func main() {
 var user User
 user.init("nick", 18, "man")
 //(&user).init("nick", 18, "man")
 name := user.GetName()
 fmt.Println(name)
}

Anonymous fields

If there is a conflict, the outermost priority


type User struct {
 Name stirng
 Age int  
}

type Lover struct {
  User
  sex time.Time
  int
  Age int
}

inheritance & Multiple inheritance

A struct inherits multiple structs and accesses a passing point. Inherit fields and methods.

You can use aliases such as u1 (user1) below and visit ES69en.u1.Age.

If the inherited structure has the same field, an error will be reported if accessed through ES74en.name and must be accessed through ES76en.user1.name.


type user1 struct {
 name string
 Age int
}

type user2 struct {
 name string
 age int
 sex time.Time
}

type User struct {
 u1 user1 // The alias 
 user2
 Name string
 Age int
}

func main() {
 var user User
 user.Name = "nick"
 user.u1.Age = 18
 fmt.Println(user) //{{ 18} { 0 {0 0 <nil>}} nick 0}
}

tag

In go, initial case has a special syntactic meaning and cannot be referenced outside of a lowercase package. Due to the need to interact with other systems, such as converting to json format. At this point, if you use the property name as the key value, it may not be 1, which will definitely meet the project requirements. tag USES specific fields as keys when converting to other data formats.


import "encoding/json"

type User struct {
 Name string `json:"userName"`
 Age int `json:"userAge"`
}

func main() {
 var user User
 user.Name = "nick"
 user.Age = 18
 
 conJson, _ := json.Marshal(user)
 fmt.Println(string(conJson)) //{"userName":"nick","userAge":0}
}

String()

If the method String() is implemented, fmt () calls String() by default.


type name1 struct {
 int
 string
}

func (this *name1) String() string {
 return fmt.Sprintf("This is String(%s).", this.string)
}

func main() {
 n := new(name1)
 fmt.Println(n) //This is String().
 n.string = "suoning"
 d := fmt.Sprintf("%s", n) //This is String(suoning).
 fmt.Println(d)
}

Interface Interface

The Interface type can define a set of methods, but these do not need to be implemented. And interface cannot contain any variables.

The interface type defaults to 1 pointer.

Interface definition


var user User
var user1 *User = &User{}
var user2 *User = new(User)
0

Interface implementation

The interface in Golang does not need to be displayed by the implementation. As long as a variable contains all the methods in the interface type, that variable implements the interface. Therefore, there is no keyword similar to implement in golang;

If a variable contains more than one method of type interface, the variable implements more than one interface. If a variable contains only 1 square method of interface, then the variable does not implement this interface.

Empty interface Interface{} : Empty interfaces have no methods, so all types implement empty interfaces.


var a int
var b interface{} // Empty interface 
b = a

polymorphism

Various forms of one kind of thing can be operated according to the interface of Unity 1.

Chestnut:


type Car interface {
 NameGet() string
 Run(n int)
 Stop()
}

type BMW struct {
 Name string
}
func (this *BMW) NameGet() string {
 return this.Name
}
func (this *BMW) Run(n int) {
 fmt.Printf("BMW is running of num is %d \n", n)
}
func (this *BMW) Stop() {
 fmt.Printf("BMW is stop \n")
}

type Benz struct {
 Name string
}
func (this *Benz) NameGet() string {
 return this.Name
}
func (this *Benz) Run(n int) {
 fmt.Printf("Benz is running of num is %d \n", n)
}
func (this *Benz) Stop() {
 fmt.Printf("Benz is stop \n")
}
func (this *Benz) ChatUp() {
 fmt.Printf("ChatUp \n")
}

func main() {
 var car Car
 fmt.Println(car) // <nil>

 var bmw BMW = BMW{Name: " BMW "}
 car = &bmw
 fmt.Println(car.NameGet()) // BMW 
 car.Run(1)     //BMW is running of num is 1
 car.Stop()     //BMW is stop

 benz := &Benz{Name: " Big rush "}
 car = benz
 fmt.Println(car.NameGet()) // Big rush 
 car.Run(2)     //Benz is running of num is 2
 car.Stop()     //Benz is stop
 //car.ChatUp() //ERROR: car.ChatUp undefined (type Car has no field or method ChatUp)
}

Interface nested

1 interface can be nested in another interface.

That is, a method that needs to implement two interfaces.


var user User
var user1 *User = &User{}
var user2 *User = new(User)
3

Types of assertions

Type assertion, because the interface is 1 generic, and the specific type is unknown,

If you want to convert to a specific type, you can do so by:


var user User
var user1 *User = &User{}
var user2 *User = new(User)
4

Chestnut 1:


var user User
var user1 *User = &User{}
var user2 *User = new(User)
5

Chestnut 2:


var user User
var user1 *User = &User{}
var user2 *User = new(User)
6

Chestnut 3:

Determines whether a variable implements the specified interface


type Stringer interface {
 String() string
}

type Mystruct interface {

}
type Mystruct2 struct {

}
func (this *Mystruct2) String() string {
 return ""
}

func main() {
 var v Mystruct
 var v2 Mystruct2
 v = &v2

 if sv, ok := v.(Stringer); ok {
  fmt.Printf("%v implements String(): %s\n", sv.String());
 }
}

Reflection reflect

The reflect package implements runtime reflection, allowing programs to manipulate objects of any type.

Typical usage is to store 1 value with static type interface{},

This function returns a value of type Type by calling TypeOf to get its dynamic type information.

A call to the ValueOf function returns a value of type Value, which represents the data at run time.

func TypeOf(i interface{}) Type

TypeOf returns the type of value saved in the interface, and TypeOf(nil) returns nil.

func ValueOf(i interface{}) Value

ValueOf returns 1 Value initialized to the specific value held by the i interface, and ValueOf(nil) returns Value zero.

reflect.Value.Kind

Gets the category of the variable, returning 1 constant


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
)

reflect.Value.Kind() The constant returned by the 

reflect.Value.Interface()

Cast to type interface{}

The variable < -- > Interface{} < -- > Reflect. Value 】

Get the value of the variable:


var user User
var user1 *User = &User{}
var user2 *User = new(User)
9

To change the value of a variable by reflecting it


reflect.Value.SetXX Related methods, such as :
reflect.Value.SetInt() , set the integer 
reflect.Value.SetFloat() , set the floating-point number 
reflect.Value.SetString() , set the string 

Chestnut 1


import "reflect"

func main() {
 var x float64 = 5.21
 fmt.Println("type:", reflect.TypeOf(x)) //type: float64

 v := reflect.ValueOf(x)
 fmt.Println("value:", v)   //value: 5.21
 fmt.Println("type:", v.Type()) //type: float64
 fmt.Println("kind:", v.Kind()) //kind: float64
 fmt.Println("value:", v.Float()) //value: 5.21

 fmt.Println(v.Interface())     //5.21
 fmt.Printf("value is %1.1e\n", v.Interface()) //value is 5.2e+00
 y := v.Interface().(float64)
 fmt.Println(y) //5.21
}

Chestnut 2 (Modified value)

SetXX(x) since it is passing a copy of the value of x, SetXX cannot change x. Changing x must pass a pointer to x to the function, SetXX( & x).


// Error code!! 
//panic: reflect: reflect.Value.SetFloat using unaddressable value
func main() {
 var a float64
 fv := reflect.ValueOf(&a)
 fv.SetFloat(520.00)
 fmt.Printf("%v\n", a)
}

// Correct, pass pointer 
func main() {
 var a2 float64
 fv2 := reflect.ValueOf(&a2)
 fv2.Elem().SetFloat(520.00)
 fmt.Printf("%v\n", a2) //520
}

Reflection operating structure

reflect. Value. NumField() gets the number of fields in the structure

reflect.Value.Method (n).Call (nil) to invoke methods in the structure

Chestnut 1 (Operating the structure by reflection)


import "reflect"

type NotknownType struct {
 S1 string
 S2 string
 S3 string
}

func (n NotknownType) String() string {
 return n.S1 + " & " + n.S2 + " & " + n.S3
}

var secret interface{} = NotknownType{"Go", "C", "Python"}

func main() {
 value := reflect.ValueOf(secret)
 fmt.Println(value) //Go & C & Python
 typ := reflect.TypeOf(secret)
 fmt.Println(typ) //main.NotknownType

 knd := value.Kind()
 fmt.Println(knd) // struct

 for i := 0; i < value.NumField(); i++ {
  fmt.Printf("Field %d: %v\n", i, value.Field(i))
 }

 results := value.Method(0).Call(nil)
 fmt.Println(results) // [Go & C & Python]
}

Chestnut 2 (Modifying the structure by reflection)


import "reflect"

type T struct {
 A int
 B string
}

func main() {
 t := T{18, "nick"}
 s := reflect.ValueOf(&t).Elem()
 typeOfT := s.Type()

 for i := 0; i < s.NumField(); i++ {
  f := s.Field(i)
  fmt.Printf("%d: %s %s = %v\n", i,
   typeOfT.Field(i).Name, f.Type(), f.Interface())
 }

 s.Field(0).SetInt(25)
 s.Field(1).SetString("nicky")
 fmt.Println(t)
}

/*
 Output: 
0: A int = 18
1: B string = nick
{25 nicky}
*/

import "reflect"

type test struct {
 S1 string
 s2 string
 s3 string
}

var s interface{} = &test{
 S1: "s1",
 s2: "s2",
 s3: "s3",
}

func main() {
 val := reflect.ValueOf(s)
 fmt.Println(val)      //&{s1 s2 s3}
 fmt.Println(val.Elem())    //{s1 s2 s3}
 fmt.Println(val.Elem().Field(0))  //s1
 val.Elem().Field(0).SetString("hehe") //S1 A capital 
}

Chestnut 3 (internal implementation of struct tag)


package main

import (
 "fmt"
 "reflect"
)

type User struct {
 Name string `json:"user_name"`
}

func main() {
 var user User
 userType := reflect.TypeOf(user)
 jsonString := userType.Field(0).Tag.Get("json")
 fmt.Println(jsonString)  //user_name
}

Related articles: