Go type关键字(类型定义与类型别名的使用差异)用法实例探究

 更新时间:2024年01月18日 09:17:00   作者:wohu 程序员的自我进化  
这篇文章主要为大家介绍了Go type关键字(类型定义与类型别名的使用差异)用法实例探究,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

1. 类型别名定义

定义类型别名的写法为:

type TypeAlias = Type

类型别名规定:TypeAlias  只是 Type  的别名,本质上 TypeAlias  与 Type 是同一个类型,就像一个孩子小时候有小名、乳名,上学后用学名,英语老师又会给他起英文名,但这些名字都指的是他本人。

2. 类型定义

类型定义语法如下:

type newType Type

其中 newType  是一种新的类型, newType  本身依然具备 Type 类型的特性。

类型声明语句一般出现在包一级,因此如果新创建的类型名字的首字符大写,则在包外部也可以使用。

一个类型声明语句创建了一个新的类型名称,和现有类型具有相同的底层结构。新命名的类型提供了一个方法,用来分隔不同概念的类型,这样即使它们底层类型相同也是不兼容的。

为了说明类型声明,我们将不同温度单位分别定义为不同的类型:

package main

type Celsius float64    // 摄氏温度
type Fahrenheit float64 // 华氏温度
const (
    AbsoluteZeroC Celsius = -273.15 // 绝对零度
    FreezingC     Celsius = 0       // 结冰点温度
    BoilingC      Celsius = 100     // 沸水温度
)

func CToF(c Celsius) Fahrenheit {
    return Fahrenheit(c*9/5 + 32)
}

func FToC(f Fahrenheit) Celsius {
    return Celsius((f - 32) * 5 / 9)
}

我们在这个包声明了两种类型:Celsius 和 Fahrenheit 分别对应不同的温度单位。它们虽然有着相同的底层类型 float64 ,但是它们是不同的数据类型,因此它们不可以被相互比较或混在一个表达式运算。

刻意区分类型,可以避免一些像无意中使用不同单位的温度混合计算导致的错误;因此需要一个类似 Celsius(t) 或 Fahrenheit(t) 形式的显式转型操作才能将 float64 转为对应的类型。

只有当两个类型的底层基础类型相同时,才允许这种转型操作,或者是两者都是指向相同底层结构的指针类型,这些转换只改变类型而不会影响值本身。如果 x 是可以赋值给 T 类型的值,那么 x 必然也可以被转为 T 类型,但是一般没有这个必要。

底层数据类型决定了内部结构和表达方式,也决定是否可以像底层类型一样对内置运算符的支持。这意味着, Celsius 和 Fahrenheit 类型的算术运算行为和底层的 float64 类型是一样的,正如我们所期望的那样。

fmt.Printf("%g\n", BoilingC-FreezingC) // "100" °C</code><code>boilingF := CToF(BoilingC)</code><code>fmt.Printf("%g\n", boilingF-CToF(FreezingC)) // "180" °F
fmt.Printf("%g\n", boilingF-FreezingC)       // compile error: type mismatch

比较运算符==<也可以用来比较一个命名类型的变量和另一个有相同类型的变量,或有着相同底层类型的未命名类型的值之间做比较。但是如果两个值有着不同的类型,则不能直接进行比较:

var c Celsius
var f Fahrenheit
fmt.Println(c == 0)          // "true"
fmt.Println(f &gt;= 0)          // "true"
fmt.Println(c == f)          // compile error: type mismatch
fmt.Println(c == Celsius(f)) // "true"!

注意最后那个语句。尽管看起来像函数调用,但是 Celsius(f) 是类型转换操作,它并不会改变值,仅仅是改变值的类型而已。测试为真的原因是因为 c 和 g 都是零值。

命名类型还可以为该类型的值定义新的行为。这些行为表示为一组关联到该类型的函数集合,我们称为类型的方法集。

下面的声明语句, Celsius 类型的参数 c 出现在了函数名的前面,表示声明的是 Celsius 类型的一个名叫 String 的方法,该方法返回该类型对象 c 带着 °C 温度单位的字符串:

func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) }

许多类型都会定义一个 String 方法,因为当使用 fmt 包的打印方法时,将会优先使用该类型对应的 String 方法返回的结果打印。

c := FToC(212.0)
fmt.Println(c.String()) // "100°C"
fmt.Printf("%v\n", c)   // "100°C"; no need to call String explicitly
fmt.Printf("%s\n", c)   // "100°C"
fmt.Println(c)          // "100°C"
fmt.Printf("%g\n", c)   // "100"; does not call String
fmt.Println(float64(c)) // "100"; does not call String

3. 类型别名与类型定义差异

类型别名与类型定义表面上看只有一个等号的差异,那么它们之间实际的区别有哪些呢?下面通过一段代码来理解。

package main
import (
    "fmt"
)
// 将NewInt定义为int类型
// 通过 type 关键字的定义,NewInt 会形成一种新的类型,NewInt 本身依然具备 int 类型的特性。
type NewInt int
// 将int取一个别名叫IntAlias, 将 IntAlias 设置为 int 的一个别名,使 IntAlias 与 int 等效。
type IntAlias = int
func main() {
    // 将a声明为NewInt类型
    var a NewInt
    // 查看a的类型名
    fmt.Printf("a type: %T\n", a)   // a type: main.NewInt
    // 将 b 声明为IntAlias类型
    var b IntAlias
    // 查看b的类型名
    fmt.Printf("b type: %T\n", b)   // b type: int
}

结果显示 a 的类型是 main.NewInt ,表示 main 包下定义的 NewInt  类型,b 类型是 int , IntAlias  类型只会在代码中存在,编译完成时,不会有 IntAlias  类型。

4. 非本地类型不能定义方法

能够随意地为各种类型起名字,是否意味着可以在自己包里为这些类型任意添加方法呢?参见下面的代码演示:

package main
import (
    "time"
)
// 定义time.Duration的别名为MyDuration
type MyDuration = time.Duration
// 为 MyDuration 添加一个方法
func (m MyDuration) EasySet(a string) {
}
func main() {
}

错误信息:

./hello.go:11:6: cannot define new methods on non-local type time.Duration

编译器提示:不能在一个非本地的类型 time.Duration  上定义新方法,非本地类型指的就是 time.Duration  不是在 main  包中定义的,而是在 time  包中定义的,与 main  包不在同一个包中,因此不能为不在一个包中的类型定义方法。修改方案为将第 8 行类型别名修改为类型定义,如下:

type MyDuration time.Duration

以上就是Go type关键字(类型定义与类型别名的使用差异)用法实例探究的详细内容,更多关于Go type关键字的资料请关注脚本之家其它相关文章!

相关文章

  • 详细介绍Go语言之数组与切片

    详细介绍Go语言之数组与切片

    这篇文章介绍Go语言之数组与切片,数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可是任意的原始类型如整形、字符串或自定义类型。切片是数组的一个引用,因此切片是引用类型,在进行传递时,遵守引用传递的机制,下面我们就来详细了解一下该内容
    2021-10-10
  • 深入解析如何基于go-retry构建灵活安全和高效的重试逻辑

    深入解析如何基于go-retry构建灵活安全和高效的重试逻辑

    本文将深入解析 sethvargo/go-retry 这个轻量且强大的 Go 重试库,带大家从原理到实践,构建灵活、安全、高效的重试逻辑,感兴趣的小伙伴可以了解下
    2025-11-11
  • 后端golang项目一键打包部署方案

    后端golang项目一键打包部署方案

    本文介绍了如何为Go语言Gin项目添加一键打包并自动部署功能,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2026-05-05
  • 使用gin框架搭建简易服务的实现方法

    使用gin框架搭建简易服务的实现方法

    go语言web框架挺多的,本文就介绍了一下如何使用gin框架搭建简易服务的实现方法,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-12-12
  • 如何使用golang实现traceroute

    如何使用golang实现traceroute

    这篇文章主要介绍了如何使用golang实现traceroute,该工具在linux环境下的命令是traceroute或者tracepath,在windows下命令是tracert,本文给大家详细讲解需要的朋友可以参考下
    2023-04-04
  • 一文教你如何用好GO语言变长参数

    一文教你如何用好GO语言变长参数

    对于函数重载相信编码过的 xdm 肯定不会陌生,那么我们一起分别来看看 C 语言,C++ 语言,GO 语言 如何去模拟和使用重载,感兴趣的可以学习一下
    2023-09-09
  • 解决golang http重定向失效的问题

    解决golang http重定向失效的问题

    这篇文章主要介绍了解决golang http重定向失效的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • golang 使用time包获取时间戳与日期格式化操作

    golang 使用time包获取时间戳与日期格式化操作

    这篇文章主要介绍了golang 使用time包获取时间戳与日期格式化操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • golang的csp模型具体使用

    golang的csp模型具体使用

    本文主要介绍了golang的csp模型具体使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-12-12
  • GO语言中gox交叉编译的实现

    GO语言中gox交叉编译的实现

    本文主要介绍了GO语言中gox交叉编译的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-11-11

最新评论