zoukankan      html  css  js  c++  java
  • [Golang学习笔记] 06 程序实体3 类型断言和类型转换

    类型断言:

    语法:
    <目标类型的值>,<布尔参数> := <表达式>.( 目标类型 ) // 安全类型断言
    <目标类型的值> := <表达式>.( 目标类型 )  //非安全类型断言

    x.(T),这里x表示一个接口的类型,T表示一个类型(也可为接口类型)。
    一个类型断言检查一个接口对象x的动态类型是否和断言的类型T匹配。

    类型断言分两种情况:
    第一种,如果断言的类型T是一个具体类型,类型断言x.(T)就检查x的动态类型是否和T的类型相同。

    如果这个检查成功了,类型断言的结果是一个类型为T的对象,该对象的值为接口变量x的动态值。换句话说,具体类型的类型断言从它的操作对象中获得具体的值。
    如果检查失败,接下来这个操作会抛出panic,除非用两个变量来接收检查结果,如:f, ok := w.(*os.File)
    第二种,如果断言的类型T是一个接口类型,类型断言x.(T)检查x的动态类型是否满足T接口。

    如果这个检查成功,则检查结果的接口值的动态类型和动态值不变,但是该接口值的类型被转换为接口类型T。换句话说,对一个接口类型的类型断言改变了类型的表述方式,改变了可以获取的方法集合(通常更大),但是它保护了接口值内部的动态类型和值的部分。
    如果检查失败,接下来这个操作会抛出panic,除非用两个变量来接收检查结果,如:f, ok := w.(io.ReadWriter)
    注意:

    如果断言的操作对象x是一个nil接口值,那么不论被断言的类型T是什么这个类型断言都会失败。
    我们几乎不需要对一个更少限制性的接口类型(更少的方法集合)做断言,因为它表现的就像赋值操作一样,除了对于nil接口值的情况。

    面试题:怎么样判断一个变量的类型?

    package main
    
    import "fmt"
    
    var container = []string{"zero", "one", "two"}
    
    func main() {
        container := map[int]string{0: "zero", 1: "one", 2: "two"}
        fmt.Printf("The element is %q.
    ", container[1])
    }

    回答:

    使用“类型断言”表达式。
    value, ok := interface{}(container).([]string)
    在赋值符号的右边,是一个类型断言表达式。
    把container变量的值转换为空接口值的interface{}(container)。
    以及一个用于判断前者类型是否为切片类型[]string的 .([]string)。

    解析:
    类型断言表达式的语法形式是x.(T)。其中x代表要被判定类型的值。这个值的类型必须是接口类型的,不过无关哪个接口类型。
    所以如果container不是任何的接口类型时,需要先转换成某个接口类型。
    如果是,这个断言表达是可以写成container.([]string)。

    最右边的圆括号中 []string 是一个类型字面量。类型字面量,就是用来表示数据类型本身的若干个字符。
    比如,string是表示字符类型的字面量,uint8是表示8位无符号整数类型的字面量。

    一对不包裹任何东西的花括号,既可以代表空代码块,也可以表示不包含任何内容的数据结构(数据类型)。

    struct{}表示不包含任何字段和方法的空结构体类型。
    []string{}表示空切片值(数据类型)。
    map[int]string{}表示空字典值,类型字面量是表示数据类型本身的若干个字符,如string是字符串类型的字面量,uint8表示8位无符号整数类型的字面量。
    其中[]string表示元素类型为string的切片类型;
    map[int]string表示键类型为int,值类型为string的字典类型。


    类型转换:

    语法:<结果类型> := <目标类型> ( <表达式> )
    T(x)
    T表示类型,x可以是变量,也可以是一个代表值的字面量(比如1.23和struct{})

    var a int = 9
    var b := float(a)

    类型转换是用来在不同但相互兼容的类型之间的相互转换的方式,所以,当类型不兼容的时候,是无法转换的。

    int<--->string

    //string到int
    value_int,err:=strconv.Atoi(string)
    //int到string
    str:=strconv.Itoa(value_int)

    int64<--->string

    //string到int64
    value_int64, err := strconv.ParseInt(string, 10, 64)
    //int64到string,需注意下面转换规定
    //FormatInt returns the string representation of i in the given base, for 2 <= base <= 36.
    //The result uses the lower-case letters 'a' to 'z' for digit values >= 10
    str:=strconv.FormatInt(value_int64, 10) //FormatInt第二个参数表示进制,10表示十进制

    float<--->string

    //float转string
    v := 3.1415926535
    s1 := strconv.FormatFloat(v, 'E', -1, 32)//float32 
    s2 := strconv.FormatFloat(v, 'E', -1, 64)//float64
    //第二个参数可选'f'/'e'/'E'等,含义如下:
    // 'b' (-ddddp±ddd,二进制指数)
    // 'e' (-d.dddde±dd,十进制指数)
    // 'E' (-d.ddddE±dd,十进制指数)
    // 'f' (-ddd.dddd,没有指数)
    // 'g' ('e':大指数,'f':其它情况)
    // 'G' ('E':大指数,'f':其它情况)
     
    //string转float
    s := "3.1415926535"
    v1, err := strconv.ParseFloat(v, 32)
    v2, err := strconv.ParseFloat(v, 64)

    float<--->int

    var a int64
    a = 1
    var b float64
    b = 2.000
     
    //a -- float64
    c := float64(a)
     
    //b -- int64
    d := int64(b)

    思考题:1. 你认为类型转换规则中有哪些值得注意的地方?

    1、 对于整数类型值、整数常量之间的类型转换,原则上只要源值在目标类型的可表示范围内就是合法的。
    源整数类型的可表示范围较大,而目标类型的可表示范围较小的情况,比如把int16转换为int8,如:

    var srcInt = int16(-255)
    dstInt := int8(srcInt)

    其中变量srcInt的值是int16类型的-255转化来的,而变量dstInt的值是srcInt转换来的,类型是int8。

    此例子中,int16类型比int8的范围要大,运行结果为:1.
    整数在Go中是以补码的形式存储的,补码其实就是原码个位求反再加1.
    int16类型的值-255的补码是1111111100000001,如果转换为int8类型,那么Go就会把在较高位置(最左位置)上的8位二进制直接截掉,从而得到00000001.
    又由于最左边一位是0,表示它是个正整数,而正整数的补码就等于其原码,所以这里输出1.

    2、虽然直接把一个整数值转为一个string类型的值是可行的,但是被转换的整数值必须要能代表一个有效的Unicode代码点。

    3、string类型与各种切片类型之间的互转。
    一个值从string类型向[]byte类型转换时,代表着以UTF-8编码的字符串会被拆分成独立的字节,两个字节代表一个汉字。
    一个值从string类型向[]rune类型转换时,代表着字符串会被拆分成一个个的Unicode字符,一个字符代表一个汉字。

    2. 什么是别名类型?什么是潜在类型?

    别名类型是type声明自定义类型的一种,如:

    type MyString = string

    表示MyString是string类型的别名类型,与其源类型是完全相同的。
    别名类型主要是为了代码重构而存在的。
    byte是uint8的别名类型,rune是int32的别名类型。

    潜在类型是某个类型在本质上是哪个类型或者是哪个类型的集合,如:

    type MyString2 string

    潜在类型的定义之间没有“=”号
    潜在类型的值之间是可以进行类型转换的,如本例中MyString2类型的值可以跟string类型的值互相转换,但是集合类的类型[]MyString2与[]string就不行,

    因为他们的潜在类型就不同了,分别是MyString2和string。

    即使两个类型的潜在类型相同,他们的值之间也不能进行判断或比较,他们的变量之间也不能互相赋值。

    3. 别名类型在代码重构过程中可以起到哪些作用吗?

    类型别名是 Go 1.9 版本添加的新功能。主要用于代码升级、迁移中类型的兼容性问题。
    下一篇分析类型别名和类型定义。

    总结
    类型断言表达式可用来判断变量是哪个类型,把结果赋给两个变量,要保证被判断的变量是接口类型的,这可能会用到类型转换表达式。
    要搞清楚别名类型声明与类型再定义之间的区别。

    本学习笔记仅为了总结自己学到的Go语言核心知识,方便以后回忆,文中部分内容摘录自极客时间的《Go语言核心36讲》专栏,如有侵权,请联系我删除。

     

  • 相关阅读:
    LeetCode Count of Range Sum
    LeetCode 158. Read N Characters Given Read4 II
    LeetCode 157. Read N Characters Given Read4
    LeetCode 317. Shortest Distance from All Buildings
    LeetCode Smallest Rectangle Enclosing Black Pixels
    LeetCode 315. Count of Smaller Numbers After Self
    LeetCode 332. Reconstruct Itinerary
    LeetCode 310. Minimum Height Trees
    LeetCode 163. Missing Ranges
    LeetCode Verify Preorder Serialization of a Binary Tree
  • 原文地址:https://www.cnblogs.com/fyql/p/10206426.html
Copyright © 2011-2022 走看看