類型轉換和類型斷言

来源:https://www.cnblogs.com/echojson/archive/2019/04/21/10746417.html
-Advertisement-
Play Games

概述 由於Go語言不允許隱式類型轉換,不同的類型之間的轉換必須做顯示的類型轉換。而類型轉換和類型斷言的本質,就是把一個類型轉換到另一個類型。 不過Go語言必須做顯示的類型轉換的要求也有例外的情況: 當普通 T 類型變數向 I 介面類型轉換時,是隱式轉換的(編譯時轉換);(T->I) 當 IX 介面變 ...


概述


 

 

由於Go語言不允許隱式類型轉換,不同的類型之間的轉換必須做顯示的類型轉換。而類型轉換和類型斷言的本質,就是把一個類型轉換到另一個類型。

不過Go語言必須做顯示的類型轉換的要求也有例外的情況:

  •  當普通 T 類型變數向 I 介面類型轉換時,是隱式轉換的(編譯時轉換);(T->I)
  • 當 IX 介面變數向I介面類型轉,是隱式轉換的(編譯完成時轉換的);(I->I)

 

類型之間轉換的例子

下麵是Go語言規範給出的部分例子:

*Point(p)        // same as *(Point(p))
(*Point)(p)      // p is converted to *Point
<-chan int(c)    // same as <-(chan int(c))
(<-chan int)(c)  // c is converted to <-chan int
func()(x)        // function signature func() x
(func())(x)      // x is converted to func()
(func() int)(x)  // x is converted to func() int
func() int(x)    // x is converted to func() int (unambiguous)

  

簡單的說, x需要轉換為T類型的語法是T(x)
如果對於某些地方的優先順序拿不准可以自己加()約束.

最後一個轉換就是一個容易混淆的語句, 因此需要用括弧(func() int)(x)提高優先順序.

還有一個容易混淆的地方是 只讀和只寫的通道類型 <-chan int/chan<- int .

 

 

變數類型轉換方式


 

 

Go語言的轉換分兩種:類型轉換 和 類型斷言

 

1. 類型轉換

 

go裡面的類型轉換寫法:

T(x)

  

 

(1)、語法:<結果類型> := <目標類型> ( <表達式> )

(2)、類型轉換是用來在不同但相互相容的類型之間的相互轉換的方式,所以,當類型不相容的時候,是無法轉換的。如下:

var var1 int = 7
fmt.Printf("%T->%v\n", var1, var1)  //int->7
var2 := float32(var1) 
fmt.Printf("%T->%v\n", var2, var2) //float32->7

  

值得註意的是,如果某些類型可能引起誤會,應該用括弧括起來轉換,如下:

//創建一個int變數,並獲得它的指針
var1 := new(int32)
fmt.Printf("%T->%v\n", var1, var1)
var2 := *int32(var1)
fmt.Printf("%T->%v\n", var2, var2)

  

*int32(var1)相當於*(int32(var1)),一個指針,當然不能直接轉換成一個int32類型,所以該表達式直接編譯錯誤。將該表達式改為 (*int32)(var1)就可以正常輸出了。

 

2. 類型斷言(Type Assertion)

類型斷言用於提取介面的基礎值 

go裡面的類型斷言寫法:

x.(T)

其中x為interface{}類型,T是要斷言的類型。

 

(1)語法

  <目標類型的值>,<布爾參數> := <表達式>.( 目標類型 ) // 安全類型斷言

  <目標類型的值> := <表達式>.( 目標類型 )  //非安全類型斷言

(2)類型斷言的本質,跟類型轉換類似,都是類型之間進行轉換,不同之處在於,類型斷言實在介面之間進行,相當於Java中,對於一個對象,把一種介面的引用轉換成另一種。

 

x.(T) 檢查x的動態類型是否是T,其中x必須是介面值。

 

我們先來看一個最簡單的錯誤的類型斷言:

func test() {
    var i interface{} = "kk"
    j := i.(int)
    fmt.Printf("%T->%d\n", j, j)
}

  

var i interface{} = "KK" 某種程度上相當於java中的,Object i = "KK";

現在把這個 i 轉換成 int 類型,系統內部檢測到這種不匹配,就會調用內置的panic()函數,拋出一個異常。

改一下,把 i 的定義改為:var i interface{} = 99,就沒問題了。輸出為:

int->99

  

以上是不安全的類型斷言。我們來看一下安全的類型斷言:

func test() {
    var i interface{} = "TT"
    j, b := i.(int)
    if b {
        fmt.Printf("%T->%d\n", j, j)
    } else {
        fmt.Println("類型不匹配")
    }
}

  

輸出“類型不匹配”。

 

在理解有關介面的相關轉換前,我們先要理解Go語言中的介面類型:interface{}

定義格式:

type IA interface {} //聲明瞭一個介面(沒有函數集合時,是空介面類型)

申明一個空介面類型時,我們在定義“空介面類型”的變數時,可以賦值任意類型的值,如

package main

import (
	"fmt"
)

type IA interface {} //空介面類型



func main()  {
	var a IA = 1  //int
	var b IA = 1.1  //float
	var c IA = false  //bool

	fmt.Println(a,b,c)
}

  

介面類型(interface{})作為函數形式參數時,則該函數可以接受任意類型的變數,但對於函數內部,該變數仍然為interface{}類型(空介面類型)

package main

import (
	"fmt"
)

func test(a interface{})  {
	fmt.Printf("%T->%v\n", a, a)
	
}


func main()  {
	test(1)
	test(1.1)
	test(true)
}

輸出結果:

int->1
float64->1.1
bool->true

  

什麼叫在在 函數內部,該變數任然為interface{}類型,如:

package main

import "fmt"

//用於輸出數組元素
func echoArray(a interface{}){
	for _,v:=range a {
		fmt.Print(v," ")
	}
	fmt.Println()
	return
}
func main(){
	a:=[]int{2,1,3,5,4}
	echoArray(a)
}

  

以上代碼將會報錯,因為對於echoArray()而言,a是interface{}類型,而不是[]int類型

所以前面代碼中,將echoArray()做如下修改即可:

func echoArray(a interface{}){
    b,_:=a.([]int)//通過斷言實現類型轉換
  for _,v:=range b{
    fmt.Print(v," ")
  }
  fmt.Println()
  return
}

 

註意:在使用斷言時最好用 (安全類型斷言)

b,ok:=a.([]int)
if ok{
    ...
}

b,ok:=a.([]int) if ok{ //斷言成功處理  
    ... }

 

  

的形式,這樣能根據ok的值判斷斷言是否成功。

因為斷言失敗在編譯階段不會報錯,所以很可能出現斷言失敗導致運行錯誤,而你卻遲遲找不到原因(親身經歷)。 

 

總結:“空介面類型”是動態類型的,即空介面類型的變數是任意類型的變數

  

通過上面的例子,我們對“空介面類型”有了瞭解,下麵我們就對“介面之間的轉換” 和 “介面和普通類型直接的轉換”作講解,來理解和介面之間的轉換到底是“類型轉換”還是“類型斷言”

 

介面之間轉換


 

 

 Go語言中介面的類型轉換有很多奇怪的特性: 有時候是隱式轉換, 有時候需要類型斷言。

 

如:2個介面類型:

type IA interface {}  //空介面類型
type IB interface {Foo()}  //非空介面類型

 

1.  空介面類型轉非空介面類型

IA要向IB轉換如何操作呢? 

這個操作無法在編譯期確定, 因此必然不是類型轉換.。由於2者都是介面類型, 因此肯定是類型斷言:

為什麼說“在編譯期確定”呢?通過上面的知識,我們知道 IA 是空介面類型,既然是空介面類型,說明IA對應的變數可以是任意類型,所以類型就無法確定(動態類型),所以在編譯的時候,都無法確定

 

var a A
var b = a.(B)

當然因為上面的代碼中的anil,會導致a.(B)錯誤。但是請註意: 這隻是運行錯誤, 並不是編譯錯誤!

 

結論:空介面類型轉非空介面類型時,是"類型斷言"轉換

 

2. 非空介面類型轉空介面類型

IB要向IA轉換如何操作呢? 因為IB是確定的類型,所以這個操作可以在編譯期確定(類型), 因此必然是類型轉換.

var b B
var a = A(b)

前面我們說過, Go語言的介面是隱式轉換的, 因此還可以省略強制轉換的語句:

var b B
var a = b

  

 

介面和類型之間的轉換(T(x))


 

 

 

雖然前面看到介面之間偶爾也會有類似普通類型之間的強制強制轉換語法, 
但從本意上來說介面是一個特殊的類型(和普通的類型區別).

我們先定義2個和前面的IA/IB匹配的普通類型(底層類型一樣):

 

type IA interface {}  //空介面類型
type IB interface {Foo()}  //非空介面類型

type TA int type TB int func (TB) Foo(
) {}

  

如果是TATB之間的轉換, 可以參考前面的類型之間轉換的例子
我們這裡重點關註 TA/TB 和 IA/IB 之間的轉換.

普通類型向介面類型轉換是隱式的(可以編譯期確定, 介面的隱式轉換特權):

 

var ta TA
var ia = ta
var tb TB
var ib = tb

  

 介面類型向普通類型是類型斷言(運行期確定):

 

var ia IA
var ta = ia.(TA)
var ib IB
var tb = ib.(TB)

  

類型斷言在編譯期是沒有任何保障的, 錯誤的代碼也可以編譯通過:

var ta = ib.(TA)
var tb = ia.(TB)

  

總結:


x.(T) 檢查x的動態類型是否是T,其中x必須是介面值。

 

  1. 如果T是具體類型,類型斷言檢查x的動態類型是否等於具體類型T。如果檢查成功,類型斷言返回的結果是x的動態值,其類型是T。換句話說,對介面值x斷言其動態類型是具體類型T,若成功則提取出x的具體值。如果檢查失敗則panic。
  2. 如果T是介面類型,類型斷言檢查x的動態類型是否滿足T。如果檢查成功,x的動態值不會被提取,返回值是一個類型為T的介面值。換句話說,到介面類型的類型斷言,改變了表達式的類型,改變了(通常是擴大了)可以訪問的方法,且保護了介面值內部的動態類型和值。

 

  • 無論T是什麼類型,如果x是nil介面值,則類型斷言失敗。
  • 類型斷言到一個較少限制(較少方法)的介面類型基本是不需要的,因為這個行為和賦值一樣(除了nil的情況)
  • 如果我們想知道類型斷言是否失敗,而不是失敗時觸發panic,可以使用返回兩個值的版本:y, ok := x.(T)

 


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

-Advertisement-
Play Games
更多相關文章
  • ArrayBlockingQueue的實現方式? ArrayBlockingQueue是否需要擴容? ArrayBlockingQueue有什麼缺點? ...
  • 如果你發現所有的功能都報找不到映射的錯,有可能是因為mapper文件沒有被編譯 在eclipse中,把資源文件放在src下,是可以被編譯的 但是在idea中,直接把資源文件放在src下,如果不進行設置,是不會被編譯的,idea對src下的xml等資源文件是預設不編譯的 ​ 在pom.xml中加入以下 ...
  • 1. 游戲功能和流程圖 實現功能:翻開兩個一樣的牌子就顯示,全部翻開游戲結束,設置5種圖形,7種顏色,游戲開始提示隨機8個牌子 游戲流程圖 2. 游戲配置 配置游戲目錄 配置游戲(game_conf.py) 配置顏色(color.py) 配置形狀(shape.py) 3. 游戲使用工具函數(view ...
  • 1.當前路徑及路徑下的文件 os.getcwd():查看當前所在路徑。 os.listdir(path):列舉目錄下的所有文件。返回的是列表類型。 2.os.path.splitext(path) 分離文件名與擴展名;預設返回(fname,fextension)元組,可做分片操作 >>> os.pa ...
  • 如 "abcdt" 反轉之後是 "tdcba" 思路1: 運用遞歸的方法進行反轉 假設反轉方法為 reverseString(String str)1)當字元串為空或者只有一個字元時,返回原字元2)當字元串有兩個以上(長度為len)的字元時,反轉後的字元串為 第二個字元開始的子串的反轉結果+第一個字 ...
  • 內置函數是 Python 的一大特色,用極簡的語法實現很多常用的操作。 它們預先定義在內置命名空間中,開箱即用,所見即所得。Python 被公認是一種新手友好型的語言,這種說法能夠成立,內置函數在其中起到了極關鍵的作用。 舉個例子,求字元串 x 的長度,Python 的寫法是 len(x) ,而且這 ...
  • 一、流的概念 流:數據在數據源(文件)和程式(記憶體)之間經歷的路徑。 輸入流:數據從數據源(文件)到程式(記憶體)的路徑。 輸出流:數據從程式(記憶體)到數據源(文件)的路徑。 以記憶體為參照,如果數據向記憶體流動,則是輸入流,反之則是輸出流 位元組流:FileInputStream用來讀取文件 FileOu ...
  • 選擇排序:是每一次從待排序的數據元素中選出最小(或最大)的一個元素,存放在序列的起始位置,直到全部待排序的數據元素排完。 原理:首先用第一個元素和後面的每一個元素進行比較,如果後面有比第一個元素小的就交換這兩個元素 比較下來會得到第最小的一個元素,放在第一個位置,然後依次拿著後面每一個元素依次這樣比 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...