目标
了解类型界说不仅告诉编译器怎样在内存中存储和处置惩罚数据,还对步伐计划产生深远影响:
- 内存结构:类型决定了变量的底层存储(好比占用多少字节、内存布局等)。
- 操作符与方法集:类型决定了哪些内置运算符适用于它,以及可否为该类型界说方法,从而扩展它的行为。
- 语义区分:通过界说新类型,纵然它们的底层类型雷同,也能明确区分不同概念,制止混用(例如温度单位、索引、文件描述符等)。
- 可读性与安全性:使用定名类型可以让代码语义更明确,从而低落出错的风险。
概念
- 每个变量或表达式都有一个类型,类型描述了值的属性,如数据占用的内存巨细、内部结构、支持哪些操作以及关联的方法集等。
- 一个 int 类型的变量可能用于表现索引、时间戳或月份;一个 float64 类型的变量可能表现速率或温度。只管底层都是数字,但语义上却截然不同。
- 内存与数据表现:每个数据类型在内存中的表现方式(如整数占用 32 位或 64 位)由其底层类型决定。定名类型会沿用雷同的存储方式,但在类型体系中赋予它新的含义。
- 方法与吸收者
- **方法的界说:**在 Go 中,可以为任意定名类型界说方法,使得该类型的值具有特定行为(如格式化输出)。
- 调用示例:fmt.Printf 等函数会自动调用类型的 String 方法,从而定制输出格式。
- 静态分派:fmt.Printf 等函数会自动调用类型的 String 方法,从而定制输出格式。
要点
类型声明语句
这条语句创建一个新的类型名称,其底层结构与现有类型雷同,但在类型体系中被以为是不同的。例如:
- type Celsius float64 // 摄氏温度类型
- type Fahrenheit float64 // 华氏温度类型
复制代码 纵然 Celsius 和 Fahrenheit 都基于 float64,它们是两个不同的类型,防止你无意中混用不同温度单位的数据。
- 定名规则与导出
- 假如新类型名称的首字符大写,则表现该类型对包外可见(导出)。
- Go 语言中关于 Unicode 字母的规则也使得中文定名默认不能导出(Go 1 的规则),但在将来的 Go 2 中可能会调解。
- 用途
- 语义区分:通过界说不同的定名类型(纵然底层雷同)可以让步伐更安全,不会将代表不同概念的值误用在一起。
- 简化代码:对于复杂或冗长的类型,用一个简单的名称可以使代码更清楚易读。
类型转换操作
- 操作形式
类型转换写作 T(x),它不会改变 x 的实际值,只是将它“贴上”新的类型标签
- var f Fahrenheit = CToF(BoilingC)
- fmt.Println(Celsius(f)) // 将 f 转换为 Celsius 类型
复制代码
- 限制:只有当两个类型的底层类型完全雷同时,大概都是指向雷同底层结构的指针时,才能举行转换。
- 作用
通过类型转换,可以将同样底层数据但语义不同的值显式转换,使得运算和比较变得类型安全。例如
- var c Celsius
- var f Fahrenheit
- // c == f // 编译错误:类型不匹配
- fmt.Println(c == Celsius(f)) // 显式转换后可以比较
复制代码 类型与方法
- 方法集
定名类型不仅是一个新的数据类型,还可以为该类型界说方法,这使得它拥有特定的行为。
- func (c Celsius) String() string {
- return fmt.Sprintf("%g°C", c)
- }
复制代码 这段代码为 Celsius 类型界说了一个 String 方法,在调用 fmt.Printf 时会自动调用该方法来格式化输出。(由于 fmt 包会自动调用 String 方法)(详细一点说:当一个类型实现了 String() string 方法后,它就满足了 fmt 包界说的 Stringer 接口。fmt 包在格式化输出时,会检查传入的值是否实现了该接口,假如实现了,就自动调用 String() 方法来获取该值的字符串表现。)
- c := FToC(212.0)
- fmt.Println(c.String()) // 输出 "100°C"
- fmt.Printf("%v\n", c) // 也输出 "100°C",因为 fmt 包会自动调用 String 方法
复制代码 为类型界说方法不仅增长了代码的可读性,也让类型更具语义。
怎样界说 Celsius 与 Fahrenheit 两个定名类型及它们之间的转换和方法
- // Package tempconv performs Celsius and Fahrenheit temperature computations.
- package tempconv
- import "fmt"
- type Celsius float64 // 定义摄氏温度类型
- type Fahrenheit float64 // 定义华氏温度类型
- // 常量声明
- const (
- AbsoluteZeroC Celsius = -273.15 // 绝对零度(摄氏)
- FreezingC Celsius = 0 // 冰点(摄氏)
- BoilingC Celsius = 100 // 沸点(摄氏)
- )
- // CToF 将摄氏温度转换为华氏温度
- func CToF(c Celsius) Fahrenheit {
- return Fahrenheit(c*9/5 + 32)
- }
- // FToC 将华氏温度转换为摄氏温度
- func FToC(f Fahrenheit) Celsius {
- return Celsius((f - 32) * 5 / 9)
- }
- // Celsius 的 String 方法,使其输出更友好
- func (c Celsius) String() string {
- return fmt.Sprintf("%g°C", c)
- }
复制代码
- Celsius 类型的 String 方法为其提供了定制格式,当使用 fmt 包打印时,会自动调用此方法。
类型比较与运算
- 支持内置运算符
由于 Celsius 和 Fahrenheit 的底层类型都是 float64,所以它们可以使用底层类型的算术和比较运算符。例如:
- fmt.Printf("%g\n", BoilingC-FreezingC) // 输出 "100"
- boilingF := CToF(BoilingC)
- fmt.Printf("%g\n", boilingF-CToF(FreezingC)) // 输出 "180"
复制代码 留意,直接将 Fahrenheit 与 Celsius 举行运算会导致编译错误,由于它们是不同的定名类型。
- 比较运算
只有雷同类型之间才能举行直接比较:
- var c Celsius
- var f Fahrenheit
- fmt.Println(c == 0) // "true",0 会被视为 Celsius 类型的零值
- fmt.Println(f >= 0) // "true",同理,0 对于 Fahrenheit 也是零值
- // fmt.Println(c == f) // 编译错误:类型不匹配
- fmt.Println(c == Celsius(f)) // 正确:通过类型转换后比较
复制代码 总结
- 类型界说描述了变量的内存表现、支持的运算符和方法集,同时区分不同的语义。
- 将不同的概念界说为不同的定名类型,纵然它们底层雷同,也能在编译期防止错误的混用。(例如,不警惕将温度与文件描述符混用可能引发错误,通过界说不同类型就可以制止这种情况。)
- 类型转换 T(x) 只改变 x 的类型标签而不改变底层数据。转换要求两个类型有雷同的底层类型或兼容的结构。
- 方法与类型行为:为定名类型界说方法(如 String 方法),使得在打印和其它操作时可以自动调用,加强了类型的体现力。
- 新类型继承了底层类型的算术与比较操作,但必须保证操作数类型一致。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |