作为一名从事php开发多年的我,刚开始接触golang的时候还是有很多地方转不过思路来,可能是不大习惯吧,经常会以php的思考方式去分析go的代码,结果就是有些地方不太能理解,比如变量类型之类的,在php里面是很自由的,比如原来变量a是字符串,后面被赋值为int类型,那么在php里面就不会报错,但是go就会报错,你必须重新定义一个int类型的变量来接收值。当然还有很多其他不太习惯的地方,比如go没有class, 结构体就相当于class, 又比如单引号和双引号在php里面都可以来表示字符串,但是在golang里面字符串必须由双引号括起来,等等吧,那今天我们来简单说下golang的强制类型转换。
go的类型转换分为强制类型转换类型断言

下面先来看下作为一名PHP开发人员经常会写的一段代码吧:
test.php

<?php
$a = 3;
$a = 3.5;
echo $a;

输出结果:3.5 ,这样的代码在php里面一点问题都没有,但是同样的代码我们使用go来实现下:
test.go

package main
import (
    "fmt"
)
func main() {
      a := 3
      a = 3.5
      fmt.Println("a:",a)
}

输出结果:



我们发现不能这么写,3.5被认为是一个常量。下面来写个正确版的:
test1.go

package main
import (
    "fmt"
)
func main() {
      a  := 3
      b := 5.5
      a = int(b)
      fmt.Println("a b",a,b)
}

输出结果:


我们发现这下正确了,看到此处我们会感觉go比较麻烦,但是强类型语言也有强类型语言的好处,严格的类型检查会使得程序的安全性更高等等吧,此处就不去过多讨论了,每种语言都有它适合使用的场景。

下面笔者来先来说下常见的一些强制类型转换:

  1. 字符串转整型(整型(int)转字符串)
package main
import (
       "fmt"
       "strconv"
  )
  func main(){
      //字符串转整型
      a := "123"
      b,_  := strconv.Atoi(a) 
     fmt.Println("a, b:",a,b)

     //整型转字符串
    a = 456
    b,_ := strconv.itoa(a)
    fmt.Println("a, b:",a,b)
  }

这里就不去运行了,小伙伴们感兴趣可以运行下哦

  1. 整型(int)和浮点之间的转换
   package main 
   import (
        "fmt"
   )
   func main(){
     //整型转浮点
      a :=  10
      var b float32
      b = float32(a) // 如果不转换就会报错
     fmt.Println("b:",b)
     
     //浮点转整型
      a :=  10.32
      var b int
      b = int(a) // 如果不转换就会报错
     fmt.Println("b:",b)
   }

当然golang 里面还有int64,float64之类的类型,也是同样的写法,这里就不再赘述了。

  1. 不同类型的指针之间的转换
    下面先来看一段代码:
package main

func main() {
    var a int = 10
    var p *int =&a
    var c *int64 
    c= (*int64)(p) 
}

输出结果:



我们发现虽然我们转了,但是貌似没有转过来,似乎不是这么转换的。事实也确实不是这么转换的,这里我们需要借助unsafe中的函数来实现:

package main

import "unsafe"
import "fmt"

func main() {
    var a int =10
    var b *int =&a
    var c *int64 = (*int64)(unsafe.Pointer(b))
    fmt.Println(*c)
}

输出结果:



正确!

  1. 接口类型判断(类型断言)
    接口在go里面也是一种数据类型,任何类型都可以赋值给接口,比如当我们不知道函数的参数将会是什么类型的时候,或者可能会有多种类型,那我们可以定义一个接口类型来接收这些参数,不过有的时候我们又需要知道这些参数的类型(接口的动态类型),那怎么办呢?这个时候我们就可以使用类型断言,写法:x.(T),这里的x表示一个接口类型,T表示一个类型(也可以为接口类型),一个类型断言检查一个接口对象x的动态类型是否和断言的类型T匹配。
    类型断言分两种情况:
    (1) 如果断言的T是一个具体的类型,类型断言x.(T)就检查x的动态类型是否和T的类型相同。
    检查成功: 类型断言的结果是一个类型为T的对象,该对象的值为接口变量x的动态值。换句话说,具体类型的类型断言从它的操作对象中获取具体的值。
    检查失败: 接下来这个操作会抛出panic,除非使用两个变量来接收检查结果
    (2) 如果断言的类型T是一个接口类型,类型断言x.(T) 检查x的动态类型是否满足T接口。
    检查成功:则检查结果的接口值的动态类型和动态值不变,但是该接口值的类型被转换为接口T类型
    检查失败: 这个操作会抛出panic,除非使用两个变量来接收检查结果。
    注意:如果断言的操作对象x是一个nil接口值,那么不论被断言的类型T是什么,这个类型断言都会失败
    下面笔者来举几个例子:
    具体类型的断言
package main

import "fmt"

func main() {
    var a interface{} =10
    switch a.(type){
    case int:
            fmt.Println("int")
    case float32:
            fmt.Println("string")
    }
}

输出结果:int
除了上面这种用法,还有下面这种使用方法:

package main

import "fmt"

func main() {
    var a interface{} =10
    t,ok:= a.(int)
    if ok{
        fmt.Println("int",t)
    }
    t2,ok:= a.(float32)
    if ok{
        fmt.Println("float32",t2)
    }
}

输出结果:int
这里使用了2个参数来接收返回值,避免panic,当然如果你事先就知道a的类型,那其实也不用这么麻烦,也可以直接这样写:

 package main

import "fmt"

func main() {
    var a interface{} = 10
    var b int
    b = a.(int)
    fmt.Println(b)
}

下面再来看下当T的类型为接口的情况:
下面我们需要定义两个两个接口:

type Inter1 interface {
        getName() string
}

type Inter2 interface {
        printName()
}


//person 来实现这两个接口
type Person struct {
        name string
}

func (p Person) getName() string {
        return p.name
}
func (p Person) printName() {
        fmt.Println(p.name)
}

func main() {
        var t Inter1
        t = Person{"xiaoming"}
        check(t)
}

func check(t Inter1) {
        //第一种情况(具体的类型)
        if f, ok1 := t.(Person); ok1 {
                fmt.Printf("f: %T,:%s\n", f, f.getName())
        }
        //第二种情况(接口类型)
        if t, ok2 := t.(Inter2); ok2 { //重用变量名t(无需重新声明)
                check2(t) //若类型断言为true,则新的t被转型为Inter2接口类型,但其动态类型和动态值不变
        }
        if f1, ok2 := t.(Person); ok2 {   //因t的动态类型不变,所以这里的ok2为true
           fmt.Printf("f1: %T,%s\n", f1, f1.getName())
       }
}

func check2(t Inter2) {  //参数类型必须为inter2,否则会报错
        t.printName()
}

运行结果:



好了今天的文章就到这里了,如果有啥不明白的,欢迎在下方给我留言哦,看到会及时回复的