以下代码能编译过吗?为什么?
package main
import (
"fmt"
)
type People interface {
Speak(string) string
}
type Student struct{}
func (stu *Student) Speak(think string) (talk string) {
if think == "bitch" {
talk = "You are a good boy"
} else {
talk = "hi"
}
return
}
func main() {
var peo People = Student{}
think := "bitch"
fmt.Println(peo.Speak(think))
}
,
,
,
,
,
,
,
,
,
,
,
,
,
答案
编译失败,值类型 `Student{}` 未实现接口`People`的方法,不能定义为 `People`类型。
在 golang 语言中,`Student` 和 `*Student` 是两种类型,第一个是表示 `Student` 本身,第二个是指向 `Student` 的指针。
如何改写?
将方法集改为值类型
type Student struct{}
// 使用值
func (stu Student) Speak(think string) (talk string) {
if think == "bitch" {
talk = "You are a good boy"
} else {
talk = "hi"
}
return
}
func main() {
var peo People = Student{}
think := "bitch"
fmt.Println(peo.Speak(think))
}
或者, 使用指针
type Student struct{}
func (stu *Student) Speak(think string) (talk string) {
if think == "bitch" {
talk = "You are a good boy"
} else {
talk = "hi"
}
return
}
func main() {
var peo People = &Student{} // 使用指针
think := "bitch"
fmt.Println(peo.Speak(think))
}
我们再看一道类似的题:
请问以下代码运行会输出什么? 为什么?
package main
import (
"fmt"
)
type People interface {
Show()
}
type Student struct{}
func (stu *Student) Show() {
}
func live() People {
var stu *Student
if stu == nil {
fmt.Println("C")
}
return stu
}
func main() {
p := live()
if p == nil {
fmt.Println("A")
} else {
fmt.Println("B")
}
}
请思考,答案在下文
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
,
答案
C
B
跟上一题一样,不同的是*Student
的定义后本身没有初始化值,所以 *Student
是 nil
的,但是*Student
实现了 People
接口,接口不为 nil
。
go中的接口分为两种: 空接口
和带有方法的接口
type eface struct { //空接口
_type *_type //类型信息
data unsafe.Pointer //指向数据的指针(go语言中特殊的指针类型unsafe.Pointer类似于c语言中的void*)
}
type iface struct { //带有方法的接口
tab *itab //存储type信息还有结构实现方法的集合
data unsafe.Pointer //指向数据的指针(go语言中特殊的指针类型unsafe.Pointer类似于c语言中的void*)
}
type _type struct {
size uintptr //类型大小
ptrdata uintptr //前缀持有所有指针的内存大小
hash uint32 //数据hash值
tflag tflag
align uint8 //对齐
fieldalign uint8 //嵌入结构体时的对齐
kind uint8 //kind 有些枚举值kind等于0是无效的
alg *typeAlg //函数指针数组,类型实现的所有方法
gcdata *byte
str nameOff
ptrToThis typeOff
}
type itab struct {
inter *interfacetype //接口类型
_type *_type //结构类型
link *itab
bad int32
inhash int32
fun [1]uintptr //可变大小 方法集合
}
我们将代码编译用gdb进行调试, go build -gcflags "-N -l" solution21.go
$ go build -gcflags "-N -l" solution21.go
$ gdb solution21
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from solution21...
Loading Go Runtime support.
(gdb) l
11 type Student struct{}
12
13 func (stu *Student) Show() {
14
15 }
16
17 func live() People {
18 var stu *Student
19 if stu == nil {
20 fmt.Println("C")
(gdb)
21 }
22 return stu
23 }
24
25 func main() {
26 p := live()
27 if p == nil {
28 fmt.Println("A")
29 } else {
30 fmt.Println("B")
(gdb)
31 }
32 }
(gdb)
Line number 33 out of range; /home/yqq/mine/master-go/interview/6-interview-golang/solution21.go has 32 lines.
(gdb) b 27
Breakpoint 1 at 0x47e147: file /home/yqq/mine/master-go/interview/6-interview-golang/solution21.go, line 27.
(gdb) r
Starting program: /home/yqq/mine/master-go/interview/6-interview-golang/solution21
[New LWP 24400]
[New LWP 24401]
[New LWP 24402]
[New LWP 24403]
[New LWP 24404]
C
Thread 1 "solution21" hit Breakpoint 1, main.main ()
at /home/yqq/mine/master-go/interview/6-interview-golang/solution21.go:27
27 if p == nil {
(gdb) i locals
p = {tab = 0x4b3188 <Student,main.People>, data = 0x0}
(gdb)
其中可以看到, p = {tab = 0x4b3188 <Student,main.People>, data = 0x0}
, interface
是由2部分组成的,tab
和data
, 当比较p
和nil
时当然就不相等了。
我们用gdb看看下面代码
package main
import "fmt"
func main() {
var p interface{} = nil
if p == nil {
fmt.Println("A")
} else {
fmt.Println("B")
}
var q interface{} = (*interface{})(nil)
if q == nil {
fmt.Println("C")
} else {
fmt.Println("D")
}
}
go build -gcflags "-N -l" solution22.go
编译
$ gdb solution22
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from solution22...
Loading Go Runtime support.
(gdb) l
1 package main
2
3 import "fmt"
4
5 func main() {
6 var p interface{} = nil
7 if p == nil {
8 fmt.Println("A")
9 } else {
10 fmt.Println("B")
(gdb)
11 }
12
13 var q interface{} = (*interface{})(nil)
14 if q == nil {
15 fmt.Println("C")
16 } else {
17 fmt.Println("D")
18 }
19
20 }
(gdb) b 7
Breakpoint 1 at 0x47e08c: file /home/yqq/mine/master-go/interview/6-interview-golang/solution22.go, line 7.
(gdb) b 14
Breakpoint 2 at 0x47e109: file /home/yqq/mine/master-go/interview/6-interview-golang/solution22.go, line 14.
(gdb) i b
Num Type Disp Enb Address What
1 breakpoint keep y 0x000000000047e08c in main.main
at /home/yqq/mine/master-go/interview/6-interview-golang/solution22.go:7
2 breakpoint keep y 0x000000000047e109 in main.main
at /home/yqq/mine/master-go/interview/6-interview-golang/solution22.go:14
(gdb) r
Starting program: /home/yqq/mine/master-go/interview/6-interview-golang/solution22
[New LWP 28172]
[New LWP 28173]
[New LWP 28174]
[New LWP 28175]
Thread 1 "solution22" hit Breakpoint 1, main.main ()
at /home/yqq/mine/master-go/interview/6-interview-golang/solution22.go:7
7 if p == nil {
(gdb) p p
$1 = {_type = 0x0, data = 0x0}
(gdb) c
Continuing.
A
Thread 1 "solution22" hit Breakpoint 2, main.main ()
at /home/yqq/mine/master-go/interview/6-interview-golang/solution22.go:14
14 if q == nil {
(gdb) p q
$2 = {_type = 0x483360, data = 0x0}
(gdb)
可以看到,p 打印出来的是 $1 = {_type = 0x0, data = 0x0}
q 打印出来的是 {_type = 0x483360, data = 0x0}
最后总结一下:
golang中的interface
底层是由2部分组成的,即(type,data)
,只要其中一个不是nil
, 那么,当用intefalce
与 nil
进行比较时都不相等。