无类型常量和类型推断
1 简介
本文总结了无类型常量的概念和使用。无类型常量是指没有明确类型的常量,包括无类型的布尔型、整数、字符、浮点数、复数和字符串。
2 无类型常量的例子
只有常量可以是无类型的。
Go 中的常量可以是有类型化的,也可以是非类型化的。例如,给定以下字符串文本:
"go"
有人可能会说,文字的类型是string ,但是,这在语义上是不正确的。
相反,文本是非类型化字符串常量。它是一个字符串(更准确地说,其默认类型是string ),
但它不是 Go 值,因此在键入的上下文中分配或使用它之前没有类型。这是一个微妙的区别,但理解之后变得很有用。
-
例子
func main() { const golang = "go" const typedgolang string = "go" var s string s = golang // 工作正常 s = typedgolang // 正常 fmt.Printf("%T, %v \n", s, s) type MyString string var mys MyString mys = golang //工作正常 //mys = typedgolang // 无法使用 typedgolang (type string) 作为 MyString的类型推导 fmt.Printf("%T,%v \n", mys, mys) }
当我们尝试在具有类型的上下文中分配这些常量时,这种差异就会发挥作用。
3 无类型常量
golang有六种未明确类型的常量类型,分别是无类型的布尔型、无类型的整数、无类型的字符、无类型的浮点数、无类型的复数、无类型的字符串。
通过延迟明确常量的具体类型,无类型的常量不仅可以提供更高的运算精度,而且可以直接用于更多的表达式而不需要显式的类型转换。
常量表达式
fmt.Println(YiB/ZiB) // "1024"
math.Pi无类型的浮点数常量,可以直接用于任意需要浮点数或复数的地方:
var x float32 = math.Pi
var y float64 = math.Pi
var z complex128 = math.Pi
如果math.Pi被确定为特定类型,比如float64,那么结果精度可能会不一样,同时对于需要float32或complex128类型值的地方则会强制需要一个明确的类型转换:
const Pi64 float64 = math.Pi
var x float32 = float32(Pi64)
var y float64 = Pi64
var z complex128 = complex128(Pi64)
对于常量面值,不同的写法可能会对应不同的类型。
例如0、0.0、0i和 \u0000虽然有着相同的常量值,
但是它们分别对应无类型的整数、无类型的浮点数、无类型的复数和无类型的字符等不同的常量类型。
同样,true和false也是无类型的布尔类型,字符串面值常量是无类型的字符串类型。
过除法运算符/会根据操作数的类型生成对应类型的结果。因此,不同写法的常量除法表达式可能对应不同的结果
var f float64 = 212
fmt.Println((f - 32) * 5 / 9) // "100"; (f - 32) * 5 is a float64
fmt.Println(5 / 9 * (f - 32)) // "0"; 5/9 is an untyped integer, 0
fmt.Println(5.0 / 9.0 * (f - 32)) // "100"; 5.0/9.0 is an untyped float
- 常量的隐式转换
当一个无类型的常量被赋值给一个变量的时候,就像下面的第一行语句,
或者出现在有明确类型的变量声明的右边,如下面的其余三行语句,无类型的常量将会被隐式转换为对应的类型,如果转换合法的话。
var f float64 = 3 + 0i // untyped complex -> float64
f = 2 // untyped integer -> float64
f = 1e123 // untyped floating-point -> float64
f = 'a' // untyped rune -> float64
它们相当于
var f float64 = float64(3 + 0i)
f = float64(2)
f = float64(1e123)
f = float64('a')
对于一个没有显式类型的变量声明(包括简短变量声明),常量的形式将隐式决定变量的默认类型,就像下面的例子:
i := 0 // untyped integer; implicit int(0)
r := '\000' // untyped rune; implicit rune('\000')
f := 0.0 // untyped floating-point; implicit float64(0.0)
c := 0i // untyped complex; implicit complex128(0i)
当尝试将这些无类型的常量转为一个接口值时(见第7章),这些默认类型将显得尤为重要,因为要靠它们明确接口对应的动态类型。
fmt.Printf("%T\n", 0) // "int"
fmt.Printf("%T\n", 0.0) // "float64"
fmt.Printf("%T\n", 0i) // "complex128"
fmt.Printf("%T\n", '\000') // "int32" (rune)
4 类型推断
- 值得一说的类型推断
类型是从它们的初始化表达式(类型)推断出来的。 随着 Go 1.18 中泛型的引入,Go 的类型推断能力 得到了显着的扩展。
为什么要进行类型推断?在非泛型 Go 代码中,省略类型的效果在简短的变量声明中最为明显。 这样的声明结合了类型推断和一点语法 糖——省略关键词的能力——变成一个非常紧凑的声明。 请考虑以下映射var变量声明
var m map[string]int = map[string]int{}
和
m := map[string]int{}
省略左侧的文字可以消除重复,同时:=提高可读性。
对于类型推断,类型化参数优先于非类型化参数。 仅当分配了类型参数时,才会考虑使用非类型化常量进行推理 , 还没有推断类型。 在对函数 的前三次调用中,变量确定 的推断类型: 它的类型是确定的 . 在这种情况下,类型推断将忽略非类型化常量,并且调用的行为完全相同 好像是用 显式实例化的。
如果仅使用非类型常量参数调用,它会变得更有趣。
在这种情况下,类型推断会考虑非类型化常量的默认类型。 作为快速提醒,以下是 Go 中可能的默认类型:
实例 常量类型 默认类型 顺序
true boolean 常量 bool
42 integer 常量 int 列表前端
'x' rune 常量 rune |
3.1416 floating-point 常量 float64 v
-1i complex 常量 complex128 列表后端
"gopher" string 常量 string
参数顺序依赖性。某些时候函数第一个与参数顺序依赖关系有关。 我们希望从类型推断中得到的一个重要属性是,推断出相同的类型 无论函数参数的顺序如何(和相应的参数) 该函数的每次调用中的顺序)。
另外就泛型的目前状态而言,go必须始终显式实例化泛型类型。
首先,对于类型实例化,类型推断 只有类型参数可以使用;没有其他论点,就像 函数调用。
必须始终至少提供一个类型参数 (除了类型约束只规定了一种可能的病理情况 所有类型参数的类型参数)。
类型的类型推断仅对完成部分 实例化类型,其中所有省略的类型参数都可以从 由类型约束得出的方程;即至少有两种类型 参数。go认为这不是一个常见的情况。
5 总结
这些常量可以提供更高的运算精度,并且可以直接用于更多的表达式而不需要显式的类型转换。
当无类型的常量被赋值给一个变量时,会发生隐式转换为对应的类型。类型推断是从初始化表达式中推断出类型的,而在泛型的引入中,类型推断能力得到了显着的扩展。
在类型推断中,类型化参数优先于非类型化参数。
最后,对于类型实例化,类型推断只有类型参数可以使用,必须至少提供一个类型参数。
- 点赞
- 收藏
- 关注作者
评论(0)