Go語言學習筆記(四)結構體struct & 介面Interface & 反射

来源:http://www.cnblogs.com/suoning/archive/2017/07/26/7145458.html
-Advertisement-
Play Games

加 Golang學習 QQ群共同學習進步成家立業工作 ^-^ 群號:96933959 結構體struct struct 用來自定義複雜數據結構,可以包含多個欄位(屬性),可以嵌套; go中的struct類型理解為類,可以定義方法,和函數定義有些許區別; struct類型是值類型。 struct定義 ...


加 Golang學習 QQ群共同學習進步成家立業工作 ^-^ 群號:96933959

結構體struct

struct 用來自定義複雜數據結構,可以包含多個欄位(屬性),可以嵌套;

go中的struct類型理解為類,可以定義方法,和函數定義有些許區別;

struct類型是值類型

struct定義

type User struct {
    Name string
    Age  int32
    mess string
}
var user User
var user1 *User = &User{}
var user2 *User = new(User)

struct使用

下麵示例中user1和user2為指針類型,訪問的時候編譯器會自動把 user1.Name 轉為 (*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
}

構造函數

golang中的struct沒有構造函數,可以偽造一個

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)
}

記憶體佈局

struct中的所有欄位在記憶體是連續的,佈局如下:

    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位元組為記憶體對齊

方法

方法是作用在特定類型的變數上,因此自定義類型,都可以有方法,而不僅僅是struct。

方法的訪問控制也是通過大小寫控制。

init函數是通過傳入指針實現,這樣改變struct欄位值,因為是值類型。

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)
}

匿名欄位

如果有衝突的, 則最外的優先

type User struct {
    Name stirng
    Age int        
}

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

繼承 & 多重繼承

一個結構體繼承多個結構體,訪問通過點。繼承欄位以及方法。

可以起別名,如下麵 u1(user1),訪問 user.u1.Age。

如果繼承的結構體都擁有同一個欄位,通過user.name訪問就會報錯,必須通過user.user1.name來訪問。

type user1 struct {
    name string
    Age  int
}

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

type User struct {
    u1   user1 //別名
    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

在go中,首字母大小寫有特殊的語法含義,小寫包外無法引用。由於需要和其它的系統進行數據交互,例如轉成json格式。這個時候如果用屬性名來作為鍵值可能不一定會符合項目要求。tag在轉換成其它數據格式的時候,會使用其中特定的欄位作為鍵值。

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()

如果實現了String()這個方法,那麼fmt預設會調用String()。

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類型可以定義一組方法,但是這些不需要實現。並且interface不能包含任何變數。

interface類型預設是一個指針。

Interface定義

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

Interface實現

  1. Golang中的介面,不需要顯示的實現。只要一個變數,含有介面類型中的所有方法,那麼這個變數就實現這個介面。因此,golang中沒有implement類似的關鍵字;
  2. 如果一個變數含有了多個interface類型的方法,那麼這個變數就實現了多個介面;如果一個變數只含有了1個interface的方部分方法,那麼這個變數沒有實現這個介面。
  3. 空介面 Interface{}:空介面沒有任何方法,所以所有類型都實現了空介面。
var a int
var b interface{}    //空介面
b  = a

多態

一種事物的多種形態,都可以按照統一的介面進行操作。

慄子:

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: "寶馬"}
    car = &bmw
    fmt.Println(car.NameGet()) //寶馬
    car.Run(1)                 //BMW is running of num is 1
    car.Stop()                 //BMW is stop

    benz := &Benz{Name: "大奔"}
    car = benz
    fmt.Println(car.NameGet()) //大奔
    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嵌套

一個介面可以嵌套在另外的介面。

即需要實現2個介面的方法。

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

type Used interface {
    Car
    Cheap()
}

類型斷言

類型斷言,由於介面是一般類型,不知道具體類型,

如果要轉成具體類型,可以採用以下方法進行轉換:

var t int
var x interface{}
x = t

y = x.(int)       //轉成int
y, ok = x.(int)   //轉成int,不報錯

慄子一:

func test(i interface{}) {
    // n := i.(int)
    n, ok := i.(int)
    if !ok {
        fmt.Println("error")
        return
    }
    n += 10
    fmt.Println(n)
}

func main() {
    var t1 int
    test(t1)
}

慄子二:

switch & type

type Student struct {
    Name string
}

func judgmentType(items ...interface{}) {
    for k, v := range items {
        switch v.(type) {
        case string:
            fmt.Printf("string, %d[%v]\n", k, v)
        case bool:
            fmt.Printf("bool, %d[%v]\n", k, v)
        case int, int32, int64:
            fmt.Printf("int, %d[%v]\n", k, v)
        case float32, float64:
            fmt.Printf("float, %d[%v]\n", k, v)
        case Student:
            fmt.Printf("Student, %d[%v]\n", k, v)
        case *Student:
            fmt.Printf("Student, %d[%p]\n", k, v)
        }
    }
}

func main() {
    stu1 := &Student{Name: "nick"}
    judgmentType(1, 2.2, "learing", stu1)
}

慄子三:

判斷一個變數是否實現了指定介面

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());
    }
}

 

反射 reflect

reflect包實現了運行時反射,允許程式操作任意類型的對象。

典型用法是用靜態類型interface{}保存一個值,

  通過調用TypeOf獲取其動態類型信息,該函數返回一個Type類型值。

  調用ValueOf函數返回一個Value類型值,該值代表運行時的數據。

func TypeOf(i interface{}) Type
TypeOf返回介面中保存的值的類型,TypeOf(nil)會返回nil。
func ValueOf(i interface{}) Value
ValueOf返回一個初始化為i介面保管的具體值的Value,ValueOf(nil)返回Value零值。
reflect.Value.Kind
獲取變數的類別,返回一個常量
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()方法返回的常量
reflect.Value.Interface()
轉換成interface{}類型
【變數<-->Interface{}<-->Reflect.Value】
獲取變數的值:
reflect.ValueOf(x).Int()
reflect.ValueOf(x).Float() 
reflect.ValueOf(x).String()
reflect.ValueOf(x).Bool()
通過反射的來改變變數的值
reflect.Value.SetXX相關方法,比如:
reflect.Value.SetInt(),設置整數
reflect.Value.SetFloat(),設置浮點數
reflect.Value.SetString(),設置字元串

 

慄子一

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
}

慄子二(修改值)

SetXX(x) 因為傳遞的是 x 的值的副本,所以SetXX不能夠改 x,改動 x 必須向函數傳遞 x 的指針,SetXX(&x) 。

//錯誤代碼!!!
//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)
}
//正確的,傳指針
func main() {
    var a2 float64
    fv2 := reflect.ValueOf(&a2)
    fv2.Elem().SetFloat(520.00)
    fmt.Printf("%v\n", a2)    //520
}

 

反射操作結構體

  1. reflect.Value.NumField()獲取結構體中欄位的個數
  2. reflect.Value.Method(n).Call(nil)來調用結構體中的方法

 

慄子一(通過反射操作結構體)

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]
}

 

慄子二(通過反射修改結構體)

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)
}

/*
輸出:
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大寫
}

 

慄子三(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
}

  


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 單例模式是設計模式中最簡單的形式之一。這一模式的目的是使得類的一個對象成為系統中的唯一實例。對於系統中的某些類來說,只有一個實例很重要,例如,一個系統中可以存在多個列印任務,但是只能有一個正在工作的任務;一個系統只能有一個視窗管理器或文件系統;一個系統只能有一個計時工具或ID(序號)生成器。 顯然單 ...
  • 能過資料庫的讀寫分離和使用NoSQL,以及搜索引擎後,能夠降低主庫的壓力,解決數據存儲方面的問題,不過隨著業務的繼續發展,我們的資料庫主庫還是會遇到性能瓶頸,所以為了減小資料庫主庫的壓力,我們有資料庫垂直拆分和水平拆分兩種方式。 資料庫拆分 資料庫拆分有兩種方法,垂直拆分和水平拆分。 垂直拆分 垂直 ...
  • Visual Studio 沒有提供此功能,只能用正則表達式,具體做法如下: 一、ctrl+ H 打開替換框 二、在替換框中的源中輸入 ^(?([^\r\n])\s)*\r?$\r?\n 圖如下: 完畢 ...
  • 這是什麼加密?可以吃嗎? 今天的給大家分享的應該是BASE64、MD5、AES演算法共同實現的吧,不過加密後體積會變大些,他不可以吃 一、概述 加密的流程大概就是醬紫的: 原文→BASE64編碼 →AES加密 得到密文 密碼→MD5加密 解密的流程大概是醬紫: 密碼 → MD5加密 → BASE64解 ...
  • java ee 中struts2中struts.xml中result標簽 ...
  • 1.package標簽下主要的點在namespace和name,extend上 extend 一般繼承自struts-default.xml下,繼承其攔截器及一些功能,這個已在本人的攔截器一文中有講到 namespace,主要用於約束訪問地址action前的路徑,比如http://www.baidu ...
  • 本節探討Java 8中的函數式數據處理 - Stream API,它能大大簡化常見的集合數據操作,怎麼做到的呢? ...
  • 1. 正則表達式基礎 1.1. 簡單介紹 正則表達式並不是Python的一部分。正則表達式是用於處理字元串的強大工具,擁有自己獨特的語法以及一個獨立的處理引擎,效率上可能不如str自帶的方法,但功能十分強大。得益於這一點,在提供了正則表達式的語言里,正則表達式的語法都是一樣的,區別隻在於不同的編程語 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...