
gcdata *byte
str nameOff
ptrToThis typeOff
}
Go 语言各种数据类型都是在 _type 字段的基础上,增加一些额外的字段来进行管理的:
typearraytype struct{
typ _type
elem *_type
slice *_type
lenuintptr
}
typechantype struct{
typ _type
elem *_type
dir uintptr
}
typeslicetype struct{
typ _type
elem *_type
}
typestructtype struct{
typ _type
pkgPath name
fields []structfield
}
这些数据类型的结构体定义,是反射实现的基础。
接口的动态类型和动态值
从源码里可以看到:iface包含两个字段:tab 是接口表指针,指向类型信息;data 是数据指针,则指向具体的数据。它们分别被称为动态类型和动态值。而接口值包括动态类型和动态值。
【引申1】接口类型和 nil 作比较
接口值的零值是指动态类型和动态值都为 nil。当仅且当这两部分的值都为 nil 的情况下,这个接口值就才会被认为 接口值 == nil。
来看个例子:
packagemain
import"fmt"
typeCoder interface{
code()
}
typeGopher struct{
name string
}
func(g Gopher)code(){
fmt.Printf("%s is codingn", g.name)
}
funcmain(){
varc Coder
fmt.Println(c == nil)
fmt.Printf("c: %T, %vn", c, c)
varg *Gopher
fmt.Println(g == nil)
c = g
fmt.Println(c == nil)
fmt.Printf("c: %T, %vn", c, c)
}
输出:
true
c: <nil>, <nil>
true
false
c: *main.Gopher, <nil>
i1自动拆箱变成基本类型,两基本类型比较值。根据图1,当一个对象与一个非对象比较时,需要将对象转化为原始类型(虽然与布尔类型比较时,需要先将布尔类型变成数字类型,但是接下来还是要将对象类型变成原始类型)。另外还有一个比较特殊的操作符is用来比较对象,例如按钮对象,如果对象是同一类型,结果就是真,如果对象不是同一类型,结果就是假。
【引申2】 来看一个例子,看一下它的输出:
packagemain
import"fmt"
typeMyError struct{}
func(i MyError)Error()string{
return"MyError"
}
funcmain(){
err := Process()
fmt.Println(err)
fmt.Println(err == nil)
}
funcProcess()error{
varerr *MyError = nil
returnerr
}
函数运行结果:
< nil>
false
这里先定义了一个 MyError 结构体,实现了 Error 函数,也就实现了 error 接口。Process 函数返回了一个 error 接口,这块隐含了类型转换。所以,虽然它的值是 nil,其实它的类型是 *MyError,最后和 nil 比较的时候,结果为 false。
【引申3】如何打印出接口的动态类型和值?
直接看代码:
packagemain
import(
"unsafe"
"fmt"
)
typeiface struct{
itab, data uintptr
}
funcmain(){
vara interface{} = nil
varb interface{} = (*int)(nil)
x := 5
varc interface{} = (*int)(&x)
ia := *(*iface)(unsafe.Pointer(&a))
ib := *(*iface)(unsafe.Pointer(&b))
ic := *(*iface)(unsafe.Pointer(&c))
fmt.Println(ia, ib, ic)
fmt.Println(*(*int)(unsafe.Pointer(ic.data)))
}
代码里直接定义了一个 iface 结构体,用两个指针来描述 itab 和 data,之后将 a, b, c 在内存中的内容强制解释成我们自定义的 iface。最后就可以打印出动态类型和动态值的地址。
运行结果如下:
{0 0} {17426912 0} {17426912 842350714568}
5
a 的动态类型和动态值的地址均为 0,也就是 nil;b 的动态类型和 c 的动态类型一致,都是 *int;最后,c 的动态值为 5。
编译器自动检测类型是否实现接口
经常看到一些开源库里会有一些类似下面这种奇怪的用法:
var _ io.Writer = (*myWriter)(nil)
这时候会有点懵,不知道作者想要干什么,实际上这就是此问题的答案。编译器会由此检查 *myWriter 类型是否实现了 io.Writer 接口。
来看一个例子:
packagemain
import"io"
typemyWriter struct{
}
/*func (w myWriter) Write(p []byte) (n int, err error) {
return
}*/
funcmain(){
// 检查 *myWriter 类型是否实现了 io.Writer 接口
var_ io.Writer = (*myWriter)(nil)
// 检查 myWriter 类型是否实现了 io.Writer 接口
var_ io.Writer = myWriter{}
}
注释掉为 myWriter 定义的 Write 函数后,运行程序:
src/main.go:14:6: cannot use(*myWriter)(nil) (type*myWriter) astypeio.Writer inassignment:
*myWriter does notimplement io.Writer (missingWrite method)
src/main.go:15:6: cannot usemyWriter literal (typemyWriter) astypeio.Writer inassignment:
myWriter does notimplement io.Writer (missingWrite method)
spring的事务管理机制是一种典型的策略模式,platformtransactionmanager代表事务管理接口,该接口定义了三个方法,该接口并不知道底层如何管理事务,但是它的实现类必须提供gettransaction()方法(开启事务)、commit()方法(提交事务)、rollback()方法(回滚事务)的多态实现,这样就可以用不同的实现类代表不同的事务管理策略。调用node提供的write(writer writer) 方法,使用默认方式将节点输出到流中:。例如使用writer.write方法输出内容,或者把writer交给子控件的render方法用于生成内容。
解除注释后,运行程序不报错。
实际上,上述赋值语句会发生隐式地类型转换,在转换的过程中,编译器会检测等号右边的类型是否实现了等号左边接口所规定的函数。
总结一下,可通过在代码中添加类似如下的代码,用来检测类型是否实现了接口:

io.close rescue nil。system.io.streamwriter writer =。if createprocess(nil, apath, nil, nil, false,normal_priority_class, nil, nil,vstartupinfo, vprocessinfo) thenresult := waitforsingleobject(vprocessinfo.hprocess, atimeout)else result := getlasterror。
接口的构造过程是怎样的
2).我入手的第一个控件是 button,我终于知道我的基础有多烂,很多基本的函数如getdlgitem() , subclassdlgitem() 都不知道,查资料,看源码 ,费了不少时间才基本完成button的自绘,另外自绘。项目中要用到嵌入式操作系统,考虑到免费开源,我首先想到了freertos,之前只是在使用,对于移植没有在意,今天花了一些时间进行移植,平台是stm32f429,编译器是mdk5,由于,我们不需要理会汇编部分的内容,freertos的官方源码包里面已经针对很多平台将接口对接好了,官方源码包里的demo是我们移植时的一个重要参考,里面囊括了很多平台,我们只需要找到我们 自己的平台便可轻松移植,下面是移植的过程:。#define stdimp_(iface, type, method, ...) com_declspec_nothrow type stdmethodcalltype cd3d
为了研究清楚接口是如何构造的,接下来我会拿起汇编的武器,还原背后的真相。
来看一个示例代码:
packagemain
import"fmt"
typePerson interface{
growUp()
}
typeStudent struct{
age int
}
func(p Student)growUp(){
p.age += 1
return
}
funcmain(){
varqcrao = Person(Student{age: 18})
fmt.Println(qcrao)
}
执行命令:
gotool compile -S ./src/main.go
得到 main 函数的汇编代码如下:
deb hardy main restricted deb-src hardy main restricted deb hardy-updates main restricted deb-src hardy-updates main restricted deb hardy universe deb-src hardy universe deb hardy-updates universe deb-src hardy-updates universe deb hardy multiverse deb-src hardy multiverse deb hardy-updates multiverse deb-src hardy-updates multiverse deb hardy-security main restricted deb-src hardy-security main restricted deb hardy-security universe deb-src hardy-security universe deb hardy-security multiverse deb-src hardy-security multiverse。将 heritrix-3.1.0\engine\src\main\java添加到eclipse的src目录,以及:heritrix-3.1.0\commons\src\main\java 目录 和heritrix-3.1.0\modules\src\main\java 目录。将heritrix-3.1.0-src.zip中的\engine\src\main\java添加到工程的src目录中,如果将heritrix-3.1.0\commons\src\main\java 目录 和 heritrix-3.1.0\modules\src\main\java 目录也拷贝到src下可以删除heritrix-commons-3.1.0.jar,heritrix-engine-3.1.0.jar,heritrix-modules-3.1.0.jar三个包的引用。
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-98808-1.html
连伊拉克现政府都不再信任你美爹