go语言学习笔记 — 基础 — 基本语法 — 类型相关(1):类型定义与类型别名

类型别名主要用于go代码升级、迁移中,类型的兼容性问题。这解决了代码重构中最麻烦的类型名变更问题。


1. 类型定义与类型别名

  • 类型定义

写法:

type TypeDef Type

栗子:

type byte uint8

type rune int32
  • 类型别名

写法:

type TypeAlias = Type

栗子:

type byte = uint8

type rune = int32

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


2. 区分类型定义与类型别名

  • 类型别名与类型定义的区别

类型别名与类型定义表面上看只有一个等号的差异,实际的区别有

1)将TypeDef类型定义为Type,TypeDef会形成一种新的类型,TypeDef本身依然具备Type的特性

2)将Type类型别名为TypeAlias,使用时,TypeAlias与Type等效

3)TypeAlias类型只会在代码中存在,编译完成时,不会再有TypeAlias类型

package main

import (
	"fmt"
	"testing"
)

// 把NewInt类型定义为int
type NewInt int

// 为int取一个别名IntAlias
type IntAlias = int

func main() {
	// 把a声明为NewInt类型,若此时打印a,值为0
	var a NewInt

	// 查看a的类型名
	// a type: main.NewInt
	fmt.Printf("a type: %T\n", a) 

	// 将a2声明为IntAlias类型
	var a2 IntAlias

	// 查看a2的类型名
	// a2 type: int
	fmt.Printf("a2 type: %T\n", a2) 
}

类型别名与类型定义表面上看只有一个等号的差异,实际的区别有

  • 将NewInt类型定义为int,NewInt会形成一种新的类型,NewInt本身依然具备int的特性
  • 将int类型别名为IntAlias,使用时,IntAlias与int等效
  • a的类型是main.NewInt,表示main包下定义的NewInt类型;a2类型是int。IntAlias类型只会在代码中存在,编译完成时,不会有IntAlias类型。

更多的例子:

package main

import (
	"fmt"
)

type (
	byte int8
	rune int32
	文本 string
	// byteSize int64    标记业务逻辑对应的数据类型
)

func main() {
	var a byte
	a = -120
	var b 文本            // 文本 == string
	b = "中文类型名"
	var c rune
	c = -50000

	fmt.Println(a)
	fmt.Println("文本b:" + b)
	fmt.Println(c)
}

3. 非本地类型不能添加方法

我们可以使用类型别名,为各种类型起新的名字。是否意味着,我们可以在代码里任意为这些类型添加方法?

package main

import (
    "time"
)

// 定义 time.Duration 的别名为 MyDuration 
type MyDuration = time.Duration

// 为接收器类型 MyDuration 添加一个方法
func (m MyDuration) EasySet(a string) {

}

func main(){

}

编译器提示:不能在一个非本地的类型time.Duration上定义新方法。

非本地方法指使用time.Duration的方法所在的包,也就是main 包。因为time.Duration是在time包中定义,在main包中使用。time.Duration包与main包不在同一个包中,因此不能给不在一个包中的类型定义方法。

解决这个问题有下面两种方法:

  • 将第8行修改为type MyDuration time.Duration,也就是将 MyDuration从类型别名改为类型定义
  • 将MyDuration的类型别名放在time包中

4. 在结构体成员嵌入时使用别名

类型别名作为结构体嵌入的成员

package main

import (
	"fmt"
	"reflect"
	"testing"
)

// 定义商标结构
type Brand struct {
}

// 为商标结构添加Show方法
func (t Brand) Show() {
}

// 为Brand定义一个别名FakeBrand
type FakeBrand = Brand

// 定义车辆结构
type Vehicle struct {
	// 嵌入两个结构
	FakeBrand
	Brand
}

func main() {
	// 声明变量a为车辆类型,即把Vechicle实例化为a
	var a Vehicle

	// 显式调用结构体FakeBrand的Show方法
	// 在调用Show()方法时,证明FakeBrand的本质确实是Brand类型。如果两个类型都有Show()方法,会有歧义。
	// 因此只有Brand类型有Show()方法,FakeBrand的本质是Brand类型。
	a.FakeBrand.Show()
	a.Brand.Show()

	// 使用反射,取变量a的类型反射对象,以查看其成员类型
	ta := reflect.TypeOf(a)

	// 遍历a的所有结构体成员变量
	for i := 0; i < ta.NumField(); i++ {

		// a的成员信息
		f := ta.Field(i)

		// 打印Vehicle结构体类型所有成员的字段名和类型
		fmt.Printf("FieldName: %v, FieldType: %v\n", f.Name, f.Type.Name())
	}
}

FakeBrand是Brand的一个类型别名。在Vehicle类型中入FakeBrand和Brand,并不意味着嵌入两个Brand。FakeBrand类型会以名字的方式保留在Vehicle的成员中。

如果尝试把a.FakeBrand.Show()改为a.Show(),编译器将发生报错:ambiguous selectora.Show。在调用Show()方法时,证明FakeBrand的本质确实是Brand类型。因为两个类型都有Show()方法,会发生歧义。

你可能感兴趣的:(Golang,golang,开发语言,后端)