Go 语言中的程序实体包括变量、常量、函数、结构体和接口。
变量声明:
Go 语言的类型推断可以带来哪些好处?
动态类型编程语言,如Python或Ruby,也有这种类型推断,可以让程序灵活性的明显提升。但在那些编程语言中,这种提升可以说是用程序的可维护性和运行效率换来的。
而Go 语言是静态类型的,所以一旦在初始化变量时确定了它的类型,之后就不可能再改变。这就避免了在后面维护程序时的一些问题。另外,请记住,这种类型的确定是在编译期完成的,因此不会对程序的运行效率产生任何影响。
如果只用一两句话回答这个问题的话,我想可以是这样的:Go 语言的类型推断可以明显提升程序的灵活性,使得代码重构变得更加容易,同时又不会给代码的维护带来额外负担(实际上,它恰恰可以避免散弹式的代码修改),更不会损失程序的运行效率。
变量重声明和可重名变量的区别:
-
变量重声明中的变量一定是在某一个代码块内的。注意,这里的“某一个代码块内”并不包含它的任何子代码块,否则就变成了“多个代码块之间”。而可重名变量指的正是在多个代码块之间由相同的标识符代表的变量。
-
. 变量重声明是对同一个变量的多次声明,这里的变量只有一个。而可重名变量中涉及的变量肯定是有多个的。
-
不论对变量重声明多少次,其类型必须始终一致,具体遵从它第一次被声明时给定的类型。而可重名变量之间不存在类似的限制,它们的类型可以是任意的。
-
如果可重名变量所在的代码块之间,存在直接或间接的嵌套关系,那么它们之间一定会存在“屏蔽”的现象。但是这种现象绝对不会在变量重声明的场景下出现。
“类型转换”表达式:
类型转换语法形式是T(x)。
其中的x可以是一个变量,也可以是一个代表值的字面量(比如1.23和struct{}{}),还可以是一个表达式。
补充:
struct{} 是空结构体类型,struct{}{} 是对它的实例化。这种实例化总会返回相同的值,可以把 struct{} 视为单例类型。
“类型断言”表达式:
类型断言表达式的语法形式是x.(T)。其中的x代表要被判断类型的值。这个值当下的类型必须是接口类型的,不过具体是哪个接口类型其实是无所谓的。
类型断言示例:
package main
import (
"fmt"
)
var container = []string{"zero", "one", "two"}
func main() {
container := map[int]string{0: "zero", 1: "one", 2: "two"}
// 方式1。
_, ok1 := interface{}(container).([]string)
_, ok2 := interface{}(container).(map[int]string)
if !(ok1 || ok2) {
fmt.Printf("Error: unsupported container type: %T\n", container)
return
}
fmt.Printf("The element is %q. (container type: %T)\n",
container[1], container)
// 方式2。
elem, err := getElement(container)
if err != nil {
fmt.Printf("Error: %s\n", err)
return
}
fmt.Printf("The element is %q. (container type: %T)\n",
elem, container)
}
func getElement(containerI interface{}) (elem string, err error) {
switch t := containerI.(type) {
case []string:
elem = t[1]
case map[int]string:
elem = t[1]
default:
err = fmt.Errorf("unsupported container type: %T", containerI)
return
}
return
}
别名类型和潜在类型的区别:
别名类型可以这样声明:
type MyString = string
Go 语言内建的基本类型中就存在两个别名类型。byte是uint8的别名类型,而rune是int32的别名类型。
潜在类型这样声明:
type MyString2 string // 注意,这里没有等号。
对于这里的类型再定义来说,string可以被称为MyString2的潜在类型。潜在类型的含义是,某个类型在本质上是哪个类型。
潜在类型相同的不同类型的值之间是可以进行类型转换的。因此,MyString2类型的值与string类型的值可以使用类型转换表达式进行互转。
但对于集合类的类型[]MyString2与[]string来说这样做却是不合法的,因为[]MyString2与[]string的潜在类型不同,分别是[]MyString2和[]string。另外,即使两个不同类型的潜在类型相同,它们的值之间也不能进行判等或比较,它们的变量之间也不能赋值。
类型转换转换代码示例:
package main
import "fmt"
func main() {
// 示例1。
{
type MyString = string
str := "BCD"
myStr1 := MyString(str)
myStr2 := MyString("A" + str)
fmt.Printf("%T(%q) == %T(%q): %v\n",
str, str, myStr1, myStr1, str == myStr1)
fmt.Printf("%T(%q) > %T(%q): %v\n",
str, str, myStr2, myStr2, str > myStr2)
fmt.Printf("Type %T is the same as type %T.\n", myStr1, str)
strs := []string{"E", "F", "G"}
myStrs := []MyString(strs)
fmt.Printf("A value of type []MyString: %T(%q)\n",
myStrs, myStrs)
fmt.Printf("Type %T is the same as type %T.\n", myStrs, strs)
fmt.Println()
}
// 示例2。
{
type MyString string
str := "BCD"
myStr1 := MyString(str)
myStr2 := MyString("A" + str)
_ = myStr2
//fmt.Printf("%T(%q) == %T(%q): %v\n",
// str, str, myStr1, myStr1, str == myStr1) // 这里的判等不合法,会引发编译错误。
//fmt.Printf("%T(%q) > %T(%q): %v\n",
// str, str, myStr2, myStr2, str > myStr2) // 这里的比较不合法,会引发编译错误。
fmt.Printf("Type %T is different from type %T.\n", myStr1, str)
strs := []string{"E", "F", "G"}
var myStrs []MyString
//myStrs := []MyString(strs) // 这里的类型转换不合法,会引发编译错误。
//fmt.Printf("A value of type []MyString: %T(%q)\n",
// myStrs, myStrs)
fmt.Printf("Type %T is different from type %T.\n", myStrs, strs)
fmt.Println()
}
// 示例3。
{
type MyString1 = string
type MyString2 string
str := "BCD"
myStr1 := MyString1(str)
myStr2 := MyString2(str)
myStr1 = MyString1(myStr2)
myStr2 = MyString2(myStr1)
myStr1 = str
//myStr2 = str // 这里的赋值不合法,会引发编译错误。
//myStr1 = myStr2 // 这里的赋值不合法,会引发编译错误。
//myStr2 = myStr1 // 这里的赋值不合法,会引发编译错误。
}
}