zhcn 编程语言 Golang 非公開: Go语言类型关键字(类型别名)

Go语言类型关键字(类型别名)

注意:本节内容包括Go语言较新版本中的功能,包括后续章节中描述的类型定义和嵌入结构等功能。另外,本节内容适合熟悉Go语言、对项目升级、代码重构等问题感兴趣的读者。

类型别名是 Go 1.9 中添加的新功能,主要用于解决代码升级和迁移过程中的类型兼容性问题。在C/ C++语言中,代码重构和升级可以让你使用宏来快速定义新代码,而在Go语言中没有添加宏的选项,但是重构中最麻烦的类型重命名问题就会得到解决。

在 Go 1.9 之前,定义内置类型的代码编写如下:

 type byte uint8
type rune int32 

Go 1.9 及更高版本如下所示:

 type byte = uint8
type rune = int32 

此更改是使用类型别名进行的。

区分类型别名和类型定义

以下是定义类型别名的方法:

type TypeAlias = Type

类型别名的规则:TypeAlias只是Type的别名。本质上,TypeAlias 和 Type 是同一个类型。这就像小时候有宠物名和婴儿名,然后使用学名一样。当他上学时,他的英语老师会给他起一个英文名字。

看起来类型别名和类型定义之间只有等号区别,但有什么区别呢?下面是一些代码来理解。

 package main

import(
    "fmt"
)

// NewIntをint型と定義する
type NewInt int

// intをIntAliasという別名にする
type IntAlias = int

func main() {

    // aをNewInt型として宣言する
    var a NewInt
    // aの型名を確認する
    fmt.Printf("a type: %T\n", a)

    // a2をIntAlias型として宣言する
    var a2 IntAlias
    // a2の型名を確認する
    fmt.Printf("a2 type: %T\n", a2)
} 

代码运行结果:

a type: main.NewInt
a2 type: int

代码说明如下:

  • 第 8 行将 NewInt 定义为 int 类型。这是定义类型的常用方法。 type关键字的定义使NewInt形成一个新的类型,但NewInt本身保留了int类型的特性。
  • 第11行将IntAlias设置为int的别名,使用IntAlias与int等效。
  • 第16行将a声明为NewInt类型,此时输出时a的值为0。
  • 第 18 行使用%T格式参数来打印变量 a 本身的类型。
  • 第21行声明a2为IntAlias类型,a2的值输出为0。
  • 第 23 行打印 a2 变量的类型。

结果显示a的类型为main.NewInt。这意味着主包中定义的 NewInt 类型。 a2的类型是int,IntAlias类型只存在于代码中,IntAlias不存在。编译完成后,输入类型。

无法在非本地类型上定义方法

能够自由命名不同的类型是否意味着我们可以在自己的包中为这些类型添加方法?请参阅下面的代码演示。

 package main

import(
    "time"
)

// time.Durationの別名MyDurationを定義
type MyDuration = time.Duration

// MyDurationに関数を追加する
func(m MyDuration)EasySet(a string){

}

func main(){

} 

代码说明如下:

  • 第 8 行为 time.Duration 设置一个名为 MyDuration 的类型别名。
  • 在第 11 行为此别名添加一个方法。

上述代码编译报错时,信息为:

cannot define new methods on non-local type time.Duration

编译器提示:无法在非本地类型 time.Duration 上定义新方法。非本地类型指的是 time.Duration。它不是在main包中定义的,而是在time包中定义的,与time.Duration不同。主包。由于它是一个包,因此您无法在包中未包含的类型上定义方法。

有两种方法可以解决此问题。

  • 将第 8 行更改为输入 MyDuration time.Duration。也就是说,将 MyDuration 从别名更改为类型。
  • 将 MyDuration 的别名定义放入 time 包中。
嵌入结构成员时使用别名

如果类型别名作为结构成员嵌入会怎样?请参阅下面的代码。

 package main

import (
"fmt"
"reflect"
)

// Brand構造体を定義
type Brand struct {
}

// Brand構造体にShow()メソッドを追加
func (t Brand) Show() {
}

// Brandに別名FakeBrandを定義
type FakeBrand = Brand

// Vehicle構造体を定義
type Vehicle struct {

// 2つの構造体を埋め込む
FakeBrand
Brand
}

func main() {

// 変数aをVehicle型として宣言
var a Vehicle

// FakeBrandのShowを呼び出すように指定
a.FakeBrand.Show()

// aの反射型オブジェクトを取得
ta := reflect.TypeOf(a)

// aのすべてのメンバーを反復処理
for i := 0; i<ta.NumField(); i++ {

// aのメンバー情報
f := ta.Field(i)

// メンバーのフィールド名と型を出力する
fmt.Printf("FieldName: %v, FieldType: %v\n", f.Name, f.Type.
    Name())
}
} 

这是代码输出:

FieldName: FakeBrand, FieldType: Brand
FieldName: Brand, FieldType: Brand

代码说明如下:

  • 第 9 行定义了商标的结构。
  • 在第 13 行,将 Show() 方法添加到徽标结构中。
  • 第 17 行定义了 Brand 的别名 FakeBrand。
  • 第 20-25 行定义了车辆结构 Vehicle 并嵌入了 FakeBrand 和 Brand 结构。
  • 在第 30 行,Vehicle 被实例化为 。
  • 第 33 行显式调用 Vehicle 的 FakeBrand 的 Show() 方法。
  • 第36行使用反射来获取变量a的反射类型对象并检查其成员的类型。
  • 第 39-42 行检查 a 的结构成员。
  • 第 45 行打印车辆类型所有成员的信息。

在此示例中,FakeBrand 是 Brand 的别名。在车辆中嵌入 FakeBrand 和 Brand 并不意味着嵌入两个品牌。 FakeBrand 类型以名称的形式保留给 Vehicle 成员。

如果将第 33 行更改为以下内容:

a.Show()

编译器会报错。

ambiguous selector a.Show

如果我们调用 Show() 方法,就会产生歧义,因为两种类型都有 Show() 方法,证明 FakeBrand 的本质确实是 Brand 类型。

《Go语言类型关键字(类型别名)》通俗易懂的讲解!您必须观看的最佳 2 个视频

【たった1時間で学べる】Go言語のプログラミング初心者向けの超入門講座【文字書き起こし、ソースコードも完全無料!】
https://www.youtube.com/watch?v=kPXfMFJ0oIE&pp=ygU6IEdvIOiogOiqnuOBruWei-OCreODvOODr-ODvOODiSAo5Z6L44Ko44Kk44Oq44Ki44K5KSZobD1KQQ%3D%3D
プログラマーへの道 #116 Go言語で学ぶ値とアドレスとポインタ(プログラミング入門)
https://www.youtube.com/watch?v=ciG4pku8ZcI&pp=ygU6IEdvIOiogOiqnuOBruWei-OCreODvOODr-ODvOODiSAo5Z6L44Ko44Kk44Oq44Ki44K5KSZobD1KQQ%3D%3D
注意:本节内容包括Go语言较新版本中的功能,包括后续章节中描述的类型定义和嵌入结构等功能。另外,本节内容适合熟悉Go语言、对项目升级、代码重构等问题感兴趣的读者。

类型别名是 Go 1.9 中添加的新功能,主要用于解决代码升级和迁移过程中的类型兼容性问题。在C/ C++语言中,代码重构和升级可以让你使用宏来快速定义新代码,而在Go语言中没有添加宏的选项,但是重构中最麻烦的类型重命名问题就会得到解决。

在 Go 1.9 之前,定义内置类型的代码编写如下:

 type byte uint8
type rune int32 

Go 1.9 及更高版本如下所示:

 type byte = uint8
type rune = int32 

此更改是使用类型别名进行的。

区分类型别名和类型定义

以下是定义类型别名的方法:

type TypeAlias = Type

类型别名的规则:TypeAlias只是Type的别名。本质上,TypeAlias 和 Type 是同一个类型。这就像小时候有宠物名和婴儿名,然后使用学名一样。当他上学时,他的英语老师会给他起一个英文名字。

看起来类型别名和类型定义之间只有等号区别,但有什么区别呢?下面是一些代码来理解。

 package main

import(
    "fmt"
)

// NewIntをint型と定義する
type NewInt int

// intをIntAliasという別名にする
type IntAlias = int

func main() {

    // aをNewInt型として宣言する
    var a NewInt
    // aの型名を確認する
    fmt.Printf("a type: %T\n", a)

    // a2をIntAlias型として宣言する
    var a2 IntAlias
    // a2の型名を確認する
    fmt.Printf("a2 type: %T\n", a2)
} 

代码运行结果:

a type: main.NewInt
a2 type: int

代码说明如下:

  • 第 8 行将 NewInt 定义为 int 类型。这是定义类型的常用方法。 type关键字的定义使NewInt形成一个新的类型,但NewInt本身保留了int类型的特性。
  • 第11行将IntAlias设置为int的别名,使用IntAlias与int等效。
  • 第16行将a声明为NewInt类型,此时输出时a的值为0。
  • 第 18 行使用%T格式参数来打印变量 a 本身的类型。
  • 第21行声明a2为IntAlias类型,a2的值输出为0。
  • 第 23 行打印 a2 变量的类型。

结果显示a的类型为main.NewInt。这意味着主包中定义的 NewInt 类型。 a2的类型是int,IntAlias类型只存在于代码中,IntAlias不存在。编译完成后,输入类型。

无法在非本地类型上定义方法

能够自由命名不同的类型是否意味着我们可以在自己的包中为这些类型添加方法?请参阅下面的代码演示。

 package main

import(
    "time"
)

// time.Durationの別名MyDurationを定義
type MyDuration = time.Duration

// MyDurationに関数を追加する
func(m MyDuration)EasySet(a string){

}

func main(){

} 

代码说明如下:

  • 第 8 行为 time.Duration 设置一个名为 MyDuration 的类型别名。
  • 在第 11 行为此别名添加一个方法。

上述代码编译报错时,信息为:

cannot define new methods on non-local type time.Duration

编译器提示:无法在非本地类型 time.Duration 上定义新方法。非本地类型指的是 time.Duration。它不是在main包中定义的,而是在time包中定义的,与time.Duration不同。主包。由于它是一个包,因此您无法在包中未包含的类型上定义方法。

有两种方法可以解决此问题。

  • 将第 8 行更改为输入 MyDuration time.Duration。也就是说,将 MyDuration 从别名更改为类型。
  • 将 MyDuration 的别名定义放入 time 包中。
嵌入结构成员时使用别名

如果类型别名作为结构成员嵌入会怎样?请参阅下面的代码。

 package main

import (
"fmt"
"reflect"
)

// Brand構造体を定義
type Brand struct {
}

// Brand構造体にShow()メソッドを追加
func (t Brand) Show() {
}

// Brandに別名FakeBrandを定義
type FakeBrand = Brand

// Vehicle構造体を定義
type Vehicle struct {

// 2つの構造体を埋め込む
FakeBrand
Brand
}

func main() {

// 変数aをVehicle型として宣言
var a Vehicle

// FakeBrandのShowを呼び出すように指定
a.FakeBrand.Show()

// aの反射型オブジェクトを取得
ta := reflect.TypeOf(a)

// aのすべてのメンバーを反復処理
for i := 0; i<ta.NumField(); i++ {

// aのメンバー情報
f := ta.Field(i)

// メンバーのフィールド名と型を出力する
fmt.Printf("FieldName: %v, FieldType: %v\n", f.Name, f.Type.
    Name())
}
} 

这是代码输出:

FieldName: FakeBrand, FieldType: Brand
FieldName: Brand, FieldType: Brand

代码说明如下:

  • 第 9 行定义了商标的结构。
  • 在第 13 行,将 Show() 方法添加到徽标结构中。
  • 第 17 行定义了 Brand 的别名 FakeBrand。
  • 第 20-25 行定义了车辆结构 Vehicle 并嵌入了 FakeBrand 和 Brand 结构。
  • 在第 30 行,Vehicle 被实例化为 。
  • 第 33 行显式调用 Vehicle 的 FakeBrand 的 Show() 方法。
  • 第36行使用反射来获取变量a的反射类型对象并检查其成员的类型。
  • 第 39-42 行检查 a 的结构成员。
  • 第 45 行打印车辆类型所有成员的信息。

在此示例中,FakeBrand 是 Brand 的别名。在车辆中嵌入 FakeBrand 和 Brand 并不意味着嵌入两个品牌。 FakeBrand 类型以名称的形式保留给 Vehicle 成员。

如果将第 33 行更改为以下内容:

a.Show()

编译器会报错。

ambiguous selector a.Show

如果我们调用 Show() 方法,就会产生歧义,因为两种类型都有 Show() 方法,证明 FakeBrand 的本质确实是 Brand 类型。

《Go语言类型关键字(类型别名)》通俗易懂的讲解!您必须观看的最佳 2 个视频

【たった1時間で学べる】Go言語のプログラミング初心者向けの超入門講座【文字書き起こし、ソースコードも完全無料!】
https://www.youtube.com/watch?v=kPXfMFJ0oIE&pp=ygU6IEdvIOiogOiqnuOBruWei-OCreODvOODr-ODvOODiSAo5Z6L44Ko44Kk44Oq44Ki44K5KSZobD1KQQ%3D%3D
プログラマーへの道 #116 Go言語で学ぶ値とアドレスとポインタ(プログラミング入門)
https://www.youtube.com/watch?v=ciG4pku8ZcI&pp=ygU6IEdvIOiogOiqnuOBruWei-OCreODvOODr-ODvOODiSAo5Z6L44Ko44Kk44Oq44Ki44K5KSZobD1KQQ%3D%3D