Description
- 新类型和类型别名
package main
import "fmt"
type MyInt1 int
type MyInt2 = int
func main() {
var i int =0
var i1 MyInt1 = i
var i2 MyInt2 = i
fmt.Println(i1,i2)
}
解析:type MyInt1 int
是基于int
类型创建了新类型,而type MyInt2 = int
是创建了 int
类型的别名MyInt2
。
var i1 MyInt1 = i
是将int
类型的变量赋值给MyInt1
,Go
是强类型的语言,编译不能通过,正确的做法是进行强制类型转换,即var i1 MyInt1 = MyInt1(i)
;而MyInt2
是int
的别名,能够进行赋值。
- 下面代码能否通过编译
func GetValue() int {
return 1
}
func main() {
i := GetValue()
switch i.(type) {
case int:
println("int")
case string:
println("string")
case interface{}:
println("interface")
default:
println("unknown")
}
}
结果:不能通过编译。
解析:只有接口类型才能进行类型选择。
- 下面的代码输出是什么
func main() {
a := 5
b := 8.1
fmt.Println(a + b)
}
解析:编译出错,Go
语言是强类型语言,不同类型之间不能直接相加。
- 下面的代码能正常编译吗
func (i int) PrintInt () {
fmt.Println(i)
}
func main() {
var i int = 1
i.PrintInt()
}
解析:编译错误。基于类型创建的方法必须定义在同一个包内;解决的办法可以定义一种新的类型。即:
type Myint int
func (i Myint) PrintInt () {
fmt.Println(i)
}
func main() {
var i Myint = 1
i.PrintInt()
}
- 下面的代码能正常编译吗
type People interface {
Speak(string) string
}
type Student struct{}
func (stu *Student) Speak(think string) (talk string) {
if think == "speak" {
talk = "speak"
} else {
talk = "hi"
}
return
}
func main() {
var peo People = Student{}
think := "speak"
fmt.Println(peo.Speak(think))
}
解析:编译错误。值类型 Student
没有实现接口的 Speak()
方法,而是指针类型 *Student
实现该方法。
- 判断下面代码能否通过编译
func main() {
sn1 := struct {
age int
name string
}{age: 11, name: "qq"}
sn2 := struct {
age int
name string
}{age: 11, name: "qq"}
if sn1 == sn2 {
fmt.Println("sn1 == sn2")
}
sm1 := struct {
age int
m map[string]string
}{age: 11, m: map[string]string{"a": "1"}}
sm2 := struct {
age int
m map[string]string
}{age: 11, m: map[string]string{"a": "1"}}
if sm1 == sm2 {
fmt.Println("sm1 == sm2")
}
}
-
结构体只能比较是否相等,但是不能比较大小。
-
相同类型的结构体才能够进行比较,结构体是否相同不但与属性类型有关,还与属性顺序相关。下面的
sn3
与sn1
就是不同的结构体:
sn3:= struct {
name string
age int
}{age:11,name:"qq"}
- 如果
struct
的所有成员都可以比较,则该struct
就可以通过==
或!=
进行比较是否相等,比较时逐个项进行比较,如果每一项都相等,则两个结构体才相等,否则不相等。
- 下面的代码能正常编译吗
type People interface {
Show()
}
type Student struct{}
func (stu *Student) Show() {
}
func main() {
var s *Student
if s == nil {
fmt.Println("s is nil")
} else {
fmt.Println("s is not nil")
}
var p People = s
if p == nil {
fmt.Println("p is nil")
} else {
fmt.Println("p is not nil")
}
}
解析:输出:s is nil
和 p is not nil
。当且仅当动态值和动态类型都为 nil
时,接口类型值才为 nil
。上面的代码,给变量 p
赋值之后,p
的动态值是 nil
,但是动态类型却是 *Student
,是一个 nil
指针,所以相等条件不成立。
- 下面代码输出什么
func main() {
x := interface{}(nil)
y := (*int)(nil)
a := y == x
b := y == nil
_, c := x.(interface{})
println(a, b, c)
}
输出:false true false
解析:类型断言语法:i.(Type)
,其中 i
是接口,Type
是类型或接口。编译时会自动检测 i
的动态类型与 Type
是否一致。但是,如果动态类型不存在,则断言总是失败。
- 下面代码能编译通过吗
type info struct {
result int
}
func work() (int,error) {
return 13,nil
}
func main() {
var data info
data.result, err := work()
fmt.Printf("info: %+v\n",data)
}
解析:编译失败。non-name data.result on left side of :=
。不能使用短变量声明设置结构体字段值。
修复代码:
func main() {
var data info
var err error
data.result, err = work() //ok
if err != nil {
fmt.Println(err)
return
}
fmt.Println(data)
}
- 下面这段代码输出什么
type People struct {
name string `json:"name"`
}
func main() {
js := `{
"name":"seekload"
}`
var p People
err := json.Unmarshal([]byte(js), &p)
if err != nil {
fmt.Println("err: ", err)
return
}e x
fmt.Println(p)
}
输出:struct field name has json tag but is not exported
解析:结构体访问控制,因为 name
首字母是小写,导致其他包不能访问。
- 下面这段代码输出什么
type T struct {
ls []int
}
func foo(t T) {
t.ls[0] = 100
}
func main() {
var t = T{
ls: []int{1, 2, 3},
}
foo(t)
fmt.Println(t.ls[0])
}
输出:100
解析:调用 foo()
函数时虽然是传值,但 foo()
函数中,字段 ls
依旧可以看成是指向底层数组的指针。
- 下面的代码有什么问题
type X struct {}
func (x *X) test() {
println(x)
}
func main() {
var a *X
a.test()
X{}.test()
}
解析:X{}
是不可寻址的,不能直接调用方法。在方法中,指针类型的接收者必须是合法指针(包括 nil
),或能获取实例地址。
- 下面的代码输出什么
type T struct {
x int
y *int
}
func main() {
i := 20
t := T{10,&i}
p := &t.x // 先计算 t.x ,然后得到 t.x 的地址
*p++ // 先获取 p 的值,然后累加
*p-- // 同上,做递减
t.y = p
fmt.Println(*t.y) // 取值
}
输出:10
解析:递增运算符 ++
和递减运算符 --
的优先级低于解引用运算符 *
和取址运算符 &
,解引用运算符和取址运算符的优先级低于选择器 .
中的属性选择操作符。
- 下面的代码输出什么
type N int
func (n *N) test(){
fmt.Println(*n)
}
func main() {
var n N = 10
p := &n
n++
f1 := n.test
n++
f2 := p.test
n++
fmt.Println(n)
f1()
f2()
}
输出:13 13 13
解析:当目标方法的接收者是指针类型时,那么被复制的就是指针。
- 下面哪一行代码会
panic
package main
type T struct{}
func (*T) foo() {
}
func (T) bar() {
}
type S struct {
*T
}
func main() {
s := S{}
_ = s.foo
s.foo()
_ = s.bar
}
解析:_ = s.bar
行。因为 s.bar
将被展开为 (*s.T).bar
,而 s.T
是个空指针,解引用会 panic
。
- 下面哪一行代码会
panic
func main() {
nil := 123
fmt.Println(nil)
var _ map[string]int = nil
}
解析:var _ map[string]int = nil
行。当前作用域中,预定义的 nil
被覆盖,此时 nil
是 int
类型值,不能赋值给 map
类型。
- 下面的代码输出什么
17.1
type T struct {
n int
}
func main() {
ts := [2]T{}
for i, t := range ts {
switch i {
case 0:
t.n = 3
ts[1].n = 9
case 1:
fmt.Print(t.n, " ")
}
}
fmt.Print(ts)
}
输出:0 [{0} {9}]
解析:此时 for-range
使用的是数组 ts
的副本,所以 t.n = 3
的赋值操作不会影响原数组。
17.2
type T struct {
n int
}
func main() {
ts := [2]T{}
for i, t := range &ts {
switch i {
case 0:
t.n = 3
ts[1].n = 9
case 1:
fmt.Print(t.n, " ")
}
}
fmt.Print(ts)
}
输出:9 [{0} {9}]
解析:for-range
循环中的循环变量 t
是原数组元素的副本。如果数组元素是结构体值,则副本的字段和原数组字段是两个不同的值。
17.3
type T struct {
n int
}
func main() {
ts := [2]T{}
for i := range ts[:] {
switch i {
case 0:
ts[1].n = 9
case 1:
fmt.Print(ts[i].n, " ")
}
}
fmt.Print(ts)
}
输出:9 [{0} {9}]
解析:for-range
切片时使用的是切片的副本,但不会复制底层数组,所以此副本切片与原数组共享底层数组。