首页 > 代码库 > GOLANG 反射法则

GOLANG 反射法则

译自[blog.golang.org/laws-of-reflection]

在计算机中, 反射是程序通过类型,检测到它自己的结构能力;是一种元编程程;也是一个具大的混淆点

在本文中,我们将通过解释反射是如何在GO中工作的来澄清它。每个语言的反射模式是不同的。本文着重于GO,
所以后文中的反射都是指GO中的反射

1.类型和接口
因为反射是修建于类型系统之上, 所以让我们从GO的类型开始讲吧。
GO是静态类型语言。 每个变量都有一个静态类型。 也就是说, 每一个已经类型在编译时已经固定了其类型:int, float32...等
假如我们声明如下:
type MyInt int
var i int
var j MyInt
则i的类型为int; j的类型为MyInt;变量i和j有明显不同的静态类型, 而潜在下层类型, 他们可以彼此赋值不须要转换。
还有一种重要类型是接口类型,它代表了一组固定的方法集。一个接口变量能存储任一实体值,只要它实现了接口方法集。以大家所
熟知的接口为例, io.Reader和io.Writer, 它们的类型摘自io 包。

// Reader 就是一个包含了基本读方法的接口
type Reader interface {
    Read(p []byte[(n int, err error)
}
// Writher就是一个包含了基本写方法的接口
type Writer interface{
    Write(p []byte)(n int, err error);
}

任何实现了上述读(写)方法签名的类型就说它实现了io.Reader(或者io.Writer).讨论这个的目标是, 就是指一个io.Reader类型的变量
可以带有任何值,只要它的类型中有一个Read方法
var r io.Reader
r = os.Stdin
r = bufio.NewReader(r)
r = new (bytes.Buffer)
// 等等 ....都可以的...

无论r带有什么实例值,r的类型总是io.Reader,明白这点是非常重要的;GO是静态类型,r的静态类型就是io.Reader。
另一个极其重要的接口例子就是空接口
interface{}
它代表一个空方法集。它可以被任何值满足。因为任何值都有0到多个方法
有些朋友说,GO的接口是动态类型的, 这就是错误引导。 他们是确实静态类型:一个接口类型的变量总是有一个相同的静态类型,
虽然,在运行时,一个存储在一个接口变量上的值可能会改变类型,但值总是满足接口。
我们需要准确的了解这些,因为反射和接口太相似了。

2. 接口的表达
Russ Cox写了一篇博客关于GO语言接口值的表达【research.swtch.com/interface】, 我就没有必须重复这个故事了。简单总结如下
接口类型的变量存成一个pair(对):值赋给变量和它的描述符descriptor;更准确的说,就是潜在下层的实例值实现了接口,它的类型
描述了下层实例值的完整类型。例如:
var r io.Reader
tty , err := os.OpenFile("/dev/tty",os.O_RDWR, 0)
if er != nil{
    return nil, err
}
r = tty

r包含了(值value,类型type)pair对,(tty,*os.File). 注意,*os.File类型实现了远不只Read方法;虽然接口值提供只有Read方法。
内部值包含所有关于值的类型信息,这就是为什么我们可以如下处理
var w io.Writer
w = r.(io.Writer) // 类型断言转换
这在个赋值表达式中是一个类型断言;它判言的是r里面的东西也实现了io.writer.并且我们能赋值给w, 赋值后,w包含(tty,*os.File)对。
和之前r所包含的对是一样的的。接口的静态类型确定了通过接口变量那些方法可以被调用。虽然内部实例值可能有一个更大的方法集。
接着,我们也可以
var empty interface{}
empty = w
我们的空接口值empty,再一次包含了一样的(tty,*os.File)对。这是很方便的, 空接口可以含有什么信息
一个重要的细节就是, 内部pair对总是有一个(值value,实类型concrete type)并且没有(value, 接口类型interface type)

现在我们可以谈反射了
。。。。。。。待续

GOLANG 反射法则