[Go]类型系统

本文深入探讨Go语言的类型系统,包括命名类型与未命名类型的区别,底层类型的定义,类型赋值的条件,以及类型转换的规则。详细阐述了类型相同的概念,介绍了自定义类型、接口、方法及其调用,以及方法值和方法表达式的使用。此外,还讨论了类型在方法调用时的自动转换情况。
摘要由CSDN通过智能技术生成

类型简介

Go语言数据类型简单分简单类型复合类型

在这里插入图片描述

命名类型和未命名类型

  1. 命名类型:可以通过标识符表示的类型.

    例如:Go语言基本类型中预声明的简单类型以及**自定义类型(type)**都是命名类型

  2. 未命名类型:由预声明类型,关键字和操作符组成(也称为类型字面量)

    所以

    []int//这个是未命名类型
    type mySlice []int//mySlice就是命名类型
    
  3. 注意:未命名类型 == 类型字面量 == 复合类型

底层类型

所有类型都有一个底层类型

  1. 预声明类型和类型字面量的底层类型是自身

  2. 自定义类型 type newtype oldtype中newtype的底层类型是oldtype的底层类型(逐层向下查找)

在这里插入图片描述

仔细研究 Go(golang) 类型系统 - 知乎 (zhihu.com)

底层类型在类型赋值和强制类型转换时会用到

类型相同和类型赋值

类型相同:

  1. 两个命名类型相同的条件: 两个类型声明语句相同

    var a int
    var b int
    //a 和 b类型相同
    var c A
    var d A
    //c 和 d类型相同
    
  2. 命名类型和未命名类型永远不同

  3. 两个未命名类型相同的条件:类型声明字面量相同且内部元素类型相同

  4. 别名类型相同

    type myInt1 = int //起别名--myInt1和int完全相同
    type myInt2 int//自定义类型--myInt2和int是两个类型但底层类型相同
    

类型赋值:

var a T1//a的类型是T1
var b T2//b的类型是T2
b = a//如果成功说明a可以直接赋值b
  1. T1和T2类型相同

  2. T1和T2具有相同的底层类型,且T1和T2至少有一个是未命名类型

    type mySlice []int
    var list1 mySlice //mySlice 命名类型
    var list2 []int	  //[]int   未命名类型
    list1 = list2	  //可以直接赋值
    
  3. T2是接口类型,T1是具体实例对象,且T1可以实现T2(T1的方法集大于等于T2的方法)

    // Student 结构体
    type Student struct {
    	name string
    }
    
    //方法实现
    func (s Student) eat() {}
    func (s Student) drink() {}
    
    //接口
    type inter interface {
    	eat()
    }
    
    	var stu Student //Student的方法集大于inter接口类型的方法集
    	var i inter //i是接口类型
    	i = stu //可以赋值(赋值之后i的静态类型仍是接口,但动态类型是Student,接口)
    
  4. T1和T2都是通道类型,具有相同的元素类型,且T1和T2至少有一个是未命名类型(没学到…)

  5. nil可以赋值给指针,函数,切片,字典,通道,接口类型

    var n *Student = nil
    
  6. a是一个可以表示类型T1的字面常量值

类型强制转换

Go是强类型语言,如果不满足自动类型转换的条件,则必须强制类型转换.

格式:var a T = (T)(x)

非常量类型的变量x可以强制转化并传递给类型T,需要满足如下任一条件:
(1)x可以直接赋值给T类型变量.
(2)x的类型和T具有相同的底层类型.
(3)x的类型和T都是未命名的指针类型,并且指针指向的类型具有相同的底层类型。
(4)x的类型和T都是整型,或者都是浮点型。
(5)x的类型和T都是复数类型。
(6)x是整数值或[]byte类型的值,T是string类型。
(7)x是一个字符串,T是[]byte[]rune(8)浮点型,整型之间可以强制类型转换(可能会损失数据精度)

类型方法

类型方法:Go语言面向对象编程的基础…只有命名类型才有方法

自定义类型

自定义类型的关键字type,格式: type newType oldType

newType 和 oldType 具有相同的底层类型,而且都继承了底层类型的操作集(例如底层类型是slice,map,则newType可以进行range访问),但是newType终归是一个新的命名类型.

  1. 自定义struct类型

    struct {//这是一个未命名结构体类型
    	name string
    	age int
    }
    type Student struct{//Student是一个命名结构体类型
        name string
        age int
    }
    
  2. interface{//未命名接口类型
        eat()
    }
    type name interface {//name是一个命名接口类型
    	eat()
    }
    

方法

Go语言类型方法是对类型行为的封装,GO语言的方法其实是特殊的函数,其将方法接收者作为函数第一个参数

//类型接收者是值类型
func (t typeName)methodName(paramList)(ReturnList){
    //method body
}
//类型接收者是指针类型
func (t *typeName)methodName(paramList)(ReturnList){
    //method body
}
t :接收者名称可变
typeName:命名类型的类型名
methodName:方法名
paramList:形参列表
ReturnList:返回值列表

​ 例子:

//Student类型
type Student struct {
	name string
	age  int
}

//Student类型的方法
func (s Student) eat() {
	fmt.Println(s.name, "正在吃饭")
}

方法调用

  1. 一般调用:typeInstanceName.methodName(paramList)

    typeInstanceName:类型实例名
    methodName:类型方法名
    paramList:方法实参
    
    //方法的调用
    s := Student{name: "张三", age: 19}//Student类型对象的创建和初始化
    s.eat()//调用方法
    
  2. 方法值调用

    x的静态类型是T,T存在一个方法M,则x.T就是方法值,将其赋值给一个变量,则可像一个函数名一样使用

    f := s.eat
    f()//方法值调用
    
  3. 方法表达式调用

    Student.eat(s)//因为方法其实就是特殊的函数
    
    //eat()方法转为函数
    func eat(s Student){
        fmt.Println(s.name, "正在吃饭")
    }
    

方法调用的类型转换

/*
具体类型实例变量直接调用其方法时,编译器会所调用方法进行自动转换,
即使接收者是指针的方法,仍然可以使用值类型变量进行调用。

(1)通过 类型字面量 显式地进行值调用和表达式调用,不会自动转换.
(2)通过 类型变量 进行值调用和表达式调用,在这种情况下,使用值调用方式调用时会进行自动转换,使用表达式调用方式调用时不会进行自动转换.
*/
func main() {
	//普通变量调用--自动类型转换
	var a = Data{name: "李四"}
	a.testValue(1)
	a.testPointer(1)
	(&a).testValue(2)
	(&a).testValue(2)
    /*
		Data 的方法集是testValue
		*Data 的方法集是testValue和testPointer
		注释了的都是失败的
	*/
	//1.通过类型字面量进行的两种调用--不进行自动转换
	(*Data)(&struct{ name string }{"张三"}).testPointer(3)//类型字面量 显式调用
	(*Data)(&struct{ name string }{"张三"}).testValue(3)
    
	(Data)(struct{ name string }{"张三"}).testValue(3) //
	Data.testValue(Data{name: "张三"}, 3)              //方法表达式
	//失败案例
	//Data.testPointer(Data{name: "张三"}, 3)//invalid method expression Data.testPointer
	//(Data)(struct{ name string }{"张三"}).testPointer(3)//cannot call pointer method on Data
	//2.通过类型变量进行值调用和表达式调用--值调用会自动转换,表达式调用不会自动转换
	Data.testValue(a, 4)
	//Data.testValue(&a, 4) //invalid method expression Data.testPointer
	//Data.testPointer(&a, 4)
	//Data.testPointer(a, 4)
	(*Data).testPointer(&a, 4)
	(*Data).testValue(&a, 4)
	//值调用会自动转换--下面这些本身就会自动转换所以赋值也一样
	(&a).testValue(4)
	a.testPointer(4)
	(&a).testPointer(4)
	(&a).testValue(4)
	//fmt.Println(unsafe.Sizeof(S{}))
}

type Data struct {
	name string
}

type S struct{
	n1 int
	c1 byte
	n2 int
}

func (d Data) testValue(n int) {
	fmt.Println(d.name, n)
}

func (d *Data) testPointer(n int) {
	fmt.Println(d.name, n)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值