内容简介:参考type有如下几种用法:1.定义结构体
参考 Go关键字--type ,原文除类型定义外,还介绍了type其它几种使用场合。本文只说类型定义。
type有如下几种用法:
1.定义结构体
2.定义接口
3.类型定义
4.类型别名
5.类型查询
一、类型定义--------节选自《Go语言圣经》第69页
为了说明类型声明,我们将不同温度单位分别定义为不同的类型:
// 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 // 沸水温度 ) 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转为对应的类型。Celsius(t)和Fahrenheit(t)是类型转换操作,它们并不是函数调用。类型转换不改变值本身,但是会使它们的语义发生变化。另一方面,CToF和FToC两个函数则是对不同温度单位下的温度进行换算,它们会返回不同的值。
对于每一个类型T,都有一个对应的类型转换操作T(x),用于将x转为T类型(译注:如果T是指针类型,可能会需要用小括弧包装T,比如 (*int)(0) )。只有当两个类型的底层基础类型相同时,才允许这种转型操作,或者是两者都是指向相同底层结构的指针类型,这些转换只改变类型而不会影响值本身。如果x是可以赋值给T类型的值,那么x必然也可以被转为T类型,但是一般没有这个必要。
数值类型之间的转型也是允许的,并且在字符串和一些特定类型的slice之间也是可以转换的,在下一章我们会看到这样的例子。这类转换可能改变值的表现。例如,将一个浮点数转为整数将丢弃小数部分,将一个字符串转为 []byte 类型的slice将拷贝一个字符串数据的副本。在任何情况下,运行时不会发生转换失败的错误(译注: 错误只会发生在编译阶段)。
底层数据类型决定了内部结构和表达方式,也决定是否可以像底层类型一样对内置运算符的支持。这意味着,Celsius和Fahrenheit类型的算术运算行为和底层的float64类型是一样的,正如我们所期望的那样。
fmt.Printf("%g\n", BoilingC-FreezingC) // "100" °C boilingF := CToF(BoilingC) 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 >= 0) // "true" fmt.Println(c == f) // compile error: type mismatch fmt.Println(c == Celsius(f)) // "true"!
注意最后那个语句。尽管看起来想函数调用,但是Celsius(f)是类型转换操作,它并不会改变值,仅仅是改变值的类型而已。测试为真的原因是因为c和g都是零值。
二、类型定义让代码更加简洁
使用类型定义定义出来的类型与原类型不相同,所以不能使用新类型变量赋值给原类型变量,除非使用强制类型转换。下面来看一段示例代码,根据string类型,定义一种新的类型,新类型名称是name:
type name string
为什么要使用类型定义呢?类型定义可以在原类型的基础上创造出新的类型,有些场合下可以使代码更加简洁,如下边示例代码:
package main import ( "fmt" ) // 定义一个接收一个字符串类型参数的函数类型 type handle func(str string) // exec函数,接收handle类型的参数 func exec(f handle) { f("hello") } func main() { // 定义一个函数类型变量,这个函数接收一个字符串类型的参数 var p = func(str string) { fmt.Println("first", str) } exec(p) // 匿名函数作为参数直接传递给exec函数 exec(func(str string) { fmt.Println("second", str) }) }
输出信息是:
first hello second hello
上边的示例是类型定义的一种简单应用场合,如果不使用类型定义,那么想要实现上边示例中的功能,应该怎么书写这段代码呢?
// exec函数,接收handle类型的参数 func exec(f func(str string)) { f("hello") }
exec函数中的参数类型,需要替换成func(str string)了,咋一看去也不复杂,但是假如exec接收一个需要5个参数的函数变量呢?是不是感觉参数列表就会很长了。
func exec(f func(str string, str2 string, num int, money float64, flag bool)) { f("hello") }
从上边的代码可以发现,exec函数的参数列表可读性变差了。下边再来看看使用类型定义是怎么实现这个功能:
package main import ( "fmt" ) // 定义一个需要五个参数的函数类型 type handle func(str string, str2 string, num int, money float64, flag bool) // exec函数,接收handle类型的参数 func exec(f handle) { f("hello", "world", 10, 11.23, true) } func demo(str string, str2 string, num int, money float64, flag bool) { fmt.Println(str, str2, num, money, flag) } func main() { exec(demo) }
三、 详解 Go 语言中的 time.Duration 类型
在 Time 包中,定义有一个名为 Duration 的类型和一些辅助的常量:
type Duration int64 const ( Nanosecond Duration = 1 Microsecond = 1000 * Nanosecond Millisecond = 1000 * Microsecond Second = 1000 * Millisecond Minute = 60 * Second Hour = 60 * Minute )
然后是测试代码:
func Test() { var waitFiveHundredMillisections int64 = 500 startingTime := time.Now().UTC() time.Sleep(10 * time.Millisecond) endingTime := time.Now().UTC() var duration time.Duration = endingTime.Sub(startingTime) var durationAsInt64 = int64(duration) if durationAsInt64 >= waitFiveHundredMillisections { fmt.Printf("Time Elapsed : Wait[%d] Duration[%d]\n", waitFiveHundredMillisections, durationAsInt64) } else { fmt.Printf("Time DID NOT Elapsed : Wait[%d] Duration[%d]\n", waitFiveHundredMillisections, durationAsInt64) } }
运行了这段测试代码,然后得到了下面的输出,从输出内容来看,我定义的 500 毫秒的时间已经用完了,但怎么可能。
Time Elapsed : Wait[500] Duration[10724798]
从上面的知识很容易看出问题, Duration 类型中时间的基本单位是 Nanosecond ,所以当我将一个表示 10 毫秒的 Duration 类型对象转换为 int64 类型时,我实际上得到的是 10,000,000。
改成这样测试一下
func Test() { var waitFiveHundredMillisections time.Duration = 500 * time.Millisecond startingTime := time.Now().UTC() time.Sleep(600 * time.Millisecond) endingTime := time.Now().UTC() var duration time.Duration = endingTime.Sub(startingTime) if duration >= waitFiveHundredMillisections { fmt.Printf("Wait %v\nNative [%v]\nMilliseconds [%d]\nSeconds [%.3f]\n", waitFiveHundredMillisections, duration, duration.Nanoseconds()/1e6, duration.Seconds()) } }
实际上, Duration 类型拥有一些便捷的类型转换函数,它们能将 Duration 类型转化为 Go 语言的内建类型 int64 或 float64 ,像下面这样:
func Test() { var duration_Seconds time.Duration = (1250 * 10) * time.Millisecond var duration_Minute time.Duration = 2 * time.Minute var float64_Seconds float64 = duration_Seconds.Seconds() var float64_Minutes float64 = duration_Minute.Minutes() fmt.Printf("Seconds [%.3f]\nMinutes [%.2f]\n", float64_Seconds, float64_Minutes) }
我也迅速注意到了在时间转换函数中,并没有转换毫秒值的函数,使用 Seconds 和 Minutes 函数,我得到了如下输出:
Seconds [12.500] Minutes [2.00]
但我需要转换毫秒值,为什么包里面单单没有提供毫秒值的转换呢?因为 Go 语言的设计者希望我有更多的选择,而不只是将毫秒值转换成某种单独的内建类型。下面的代码中,我将毫秒值转化为了 int64 类型和 float64 类型:
func Test() { var duration_Milliseconds time.Duration = 500 * time.Millisecond var castToInt64 int64 = duration_Milliseconds.Nanoseconds() / 1e6 var castToFloat64 float64 = duration_Milliseconds.Seconds() * 1e3 fmt.Printf("Duration [%v]\ncastToInt64 [%d]\ncastToFloat64 [%.0f]\n", duration_Milliseconds, castToInt64, castToFloat64) }
我将纳秒值除以 1e6 得到了 int64 类型表示的毫秒值,将秒值乘以 1e3 ,我得到了 float64 类型表示的毫秒值,上面代码的输出如下:
Duration [500ms] castToInt64 [500] castToFloat64 [500]
参考一下内置包time中的源码,很容易理解:
func (d Duration) Seconds() float64 { sec := d / Second nsec := d % Second return float64(sec) + float64(nsec)/1e9 }
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 番外篇2-基本规范、注释、static关键字、import关键字
- 说说iOS中的常用的关键字static ,class(仅限Swift关键字)
- Golang 关键字
- 2019 关键字
- golang关键字
- final关键字深入解析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。