当前位置: 首页 > news >正文

【Go语言圣经2.5】

目标

了解类型定义不仅告诉编译器如何在内存中存储和处理数据,还对程序设计产生深远影响:

  • 内存结构:类型决定了变量的底层存储(比如占用多少字节、内存布局等)。
  • 操作符与方法集:类型决定了哪些内置运算符适用于它,以及能否为该类型定义方法,从而扩展它的行为。
  • 语义区分:通过定义新类型,即使它们的底层类型相同,也能明确区分不同概念,避免混用(例如温度单位、索引、文件描述符等)。
  • 可读性与安全性:使用命名类型可以让代码语义更明确,从而降低出错的风险。

概念

  1. 每个变量或表达式都有一个类型,类型描述了值的属性,如数据占用的内存大小、内部结构、支持哪些操作以及关联的方法集等。
  2. 一个 int 类型的变量可能用于表示索引、时间戳或月份;一个 float64 类型的变量可能表示速度或温度。尽管底层都是数字,但语义上却截然不同。
  3. 内存与数据表示:每个数据类型在内存中的表示方式(如整数占用 32 位或 64 位)由其底层类型决定。命名类型会沿用相同的存储方式,但在类型系统中赋予它新的含义。
  4. 方法与接收者
    • **方法的定义:**在 Go 中,可以为任意命名类型定义方法,使得该类型的值具有特定行为(如格式化输出)。
    • 调用示例:fmt.Printf 等函数会自动调用类型的 String 方法,从而定制输出格式。
    • 静态分派:fmt.Printf 等函数会自动调用类型的 String 方法,从而定制输出格式。

要点

类型声明语句

type 新类型名称 底层类型

这条语句创建一个新的类型名称,其底层结构与现有类型相同,但在类型系统中被认为是不同的。例如:

type Celsius float64    // 摄氏温度类型
type Fahrenheit float64 // 华氏温度类型

即使 Celsius 和 Fahrenheit 都基于 float64,它们是两个不同的类型,防止你无意中混用不同温度单位的数据。

  • 命名规则与导出
    • 如果新类型名称的首字符大写,则表示该类型对包外可见(导出)。
    • Go 语言中关于 Unicode 字母的规则也使得中文命名默认不能导出(Go 1 的规则),但在将来的 Go 2 中可能会调整。
  • 用途
    • 语义区分:通过定义不同的命名类型(即使底层相同)可以让程序更安全,不会将代表不同概念的值误用在一起。
    • 简化代码:对于复杂或冗长的类型,用一个简单的名称可以使代码更清晰易读。

类型转换操作

  1. 操作形式

    类型转换写作 T(x),它不会改变 x 的实际值,只是将它“贴上”新的类型标签

    var f Fahrenheit = CToF(BoilingC)
    fmt.Println(Celsius(f)) // 将 f 转换为 Celsius 类型
    
    • 限制:只有当两个类型的底层类型完全相同时,或者都是指向相同底层结构的指针时,才能进行转换。
  2. 作用

    通过类型转换,可以将同样底层数据但语义不同的值显式转换,使得运算和比较变得类型安全。例如

    var c Celsius
    var f Fahrenheit
    // c == f // 编译错误:类型不匹配
    fmt.Println(c == Celsius(f)) // 显式转换后可以比较
    

类型与方法

  1. 方法集

    命名类型不仅是一个新的数据类型,还可以为该类型定义方法,这使得它拥有特定的行为。

    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)) // 正确:通过类型转换后比较
    

总结

  1. 类型定义描述了变量的内存表示、支持的运算符和方法集,同时区分不同的语义。
  2. 将不同的概念定义为不同的命名类型,即使它们底层相同,也能在编译期防止错误的混用。(例如,不小心将温度与文件描述符混用可能引发错误,通过定义不同类型就可以避免这种情况。)
  3. 类型转换 T(x) 只改变 x 的类型标签而不改变底层数据。转换要求两个类型有相同的底层类型或兼容的结构。
  4. 方法与类型行为:为命名类型定义方法(如 String 方法),使得在打印和其它操作时可以自动调用,增强了类型的表现力。
  5. 新类型继承了底层类型的算术与比较操作,但必须保证操作数类型一致。

相关文章:

  • Matlab 基于磁流变阻尼器的半主动车辆座椅悬架模糊控制研究
  • Java集合 - ArrayList
  • 向量检索在AI中的应用与技术解析
  • 数据库的高阶知识
  • 中考英语之07句子成分
  • 【leetcode hot 100 114】二叉树展开为链表
  • HTB靶机 - Dog记录
  • 【自学笔记】Solidity基础知识点总览-持续更新
  • 前端---CSS(前端三剑客)
  • Native层逆向:ARM汇编与JNI调用分析
  • 模型即产品:一场被低估的AI Agent革命正在发生
  • JavaScript 中 call 和 apply 的用法与区别
  • Qt-QChart实现折线图
  • 深入浅出矩阵的秩
  • 并发编程-
  • 数据传输对象 DTO
  • 【Kubernetes】Service 的类型有哪些?ClusterIP、NodePort 和 LoadBalancer 的区别?
  • Nginx 目录浏览功能显示的日期格式设置为数字
  • 表达式和语句的区别
  • 86.HarmonyOS NEXT 组件通信与状态共享:构建高效的组件协作机制
  • 郑州网站推广费用/谷歌浏览器安卓版下载
  • 凡客优品官方网站/seo短期培训班
  • 车轮违章查询是什么网站开发/产品软文
  • ui设计作品欣赏网站/惠州seo报价
  • 室内设计师收入高吗/在线优化seo
  • 比wordpress更好/天津seo优化