设为首页 加入收藏

TOP

Go基础系列:struct的导出和暴露问题
2018-10-31 22:08:55 】 浏览:60
Tags:基础 系列 struct 导出 暴露 问题

struct的导出和暴露问题

关于struct的导出

struct的属性是否被导出,也遵循大小写的原则:首字母大写的被导出,首字母小写的不被导出。

所以,如果struct名称首字母是小写的,这个struct不会被导出。连同它里面的字段也不会导出,即使有首字母大写的字段名。

如果struct名称首字母大写,则struct会被导出,但只会导出它内部首字母大写的字段,那些小写首字母的字段不会被导出。

也就是说,struct的导出情况是混合的。

但并非绝对如此,如果struct嵌套了,那么即使被嵌套在内部的struct名称首字母小写,也能访问到它里面首字母大写的字段。

例如:

type animal struct{
    name string
    Speak string
}
type Horse struct {
    animal
    sound string
}

Horse中嵌套的animal是小写字母开头的,但Horse是能被导出的,所以能在其它包中使用Horse struct,其他包也能访问到animal中的Speak属性。

很多时候,Horse这个名字是不安全的,因为这表示导出Horse这个struct给其他包,也就是将Horse给暴露出去了,外界可以直接打开Horse这个"黑匣子"。

但如果不将Horse导出,如何能在其它包构建出Horse实例?见下文。

不要暴露struct

很多时候,不应该将某包(如包abc)中的struct(如animal)直接暴露给其它包,暴露意味着打开了那个"黑匣子",所以struct会以小写字母开头,不将其导出。

这时在外界其它包中构建包abc的animal,就没法直接通过以下几种方式实现:

  • var xxx abc.animal
  • new(abc.animal)
  • &abc.animal{...}
  • abc.animal{...}

例如,下面的是错误的:

// abc/abc.go文件内容:
package abc

type animal struct{
    name string
    Speak string
}

// test.go内容:
package main

import "./abc"

func main() {
    // 全都错误
    var t1 abc.animal
    t2 := new(abc.animal)
    t3 := &abc.animal{}
    t4 := abc.animal{}
}

那么如何在外界构建隐藏起来的struct实例?这时可以在abc包中写一个可导出的函数,通过这个函数来构建struct实例。例如:

// abc/abc.go文件内容:
package abc

type animal struct{
    name string
    Speak string
}

func NewAnimal() *animal{
    a := new(animal)
    return a
}


// test.go内容:
package main

import (
    "fmt"
    "./abc"
)

func main() {
    t1 := abc.NewAnimal()
//  t1.name = "haha"    // 无法访问name属性
    t1.Speak = "hhhh"
    fmt.Println(t1.Speak)
}

上面的代码一切正常,在main包中可以通过NewAnimal()构建出abc包中未导出的animal struct。注意,上面NewAnimal()中是使用new()函数构造实例的,它返回的是实例的指针,至于如何构造实例,完全可以根据自己的需求,但对于struct类型来说,一般都是使用指针的,也就是完全可以将new()通用化。

由于animal中的name字段是不导出的字段,所以在外界即便是通过NewAnimal()构建出了animal实例,也无法访问该实例的name属性,所以没法为name字段赋值。换句话说,name属性永远是初始化的0值。

因此,为了让构建实例时自定义name属性,需要在构造方法NewAnimal()上指定设置给name属性的参数。修改NewAnimal()函数:

func NewAnimal(name string) *animal{
    a := new(animal)
    a.name = name
    return a
}

然后在其它包中构建animal实例:

t1 := abc.NewAnimal("longshuai")

虽然其它包中构建的animal实例已经具备了name属性,但还是无法访问该实例的name属性。所以,在abc包中继续写一个可导出的方法,该方法用于获取实例的name属性:

// abc/abc.go中添加:
func (a *animal) GetName() string {
    return a.name
}

于是外界包中可以通过这个导出的方法获取实例的name属性:

t1 := abc.NewAnimal("longshuai")
fmt.Println(abc.GetName())

实际上,上面NewAnimal()构造对象时,可以不用传递name参数,而是像GetName()一样,写一个专门的可导出方法来设置实例的name属性。改写abc/abc.go中的代码:

func NewAnimal() *animal{
    a := new(animal)
    return a
}
func (a *animal) SetName(name string){
    a.name = name
}

现在,abc/abc.go中的animal struct就完全对外隐藏了。


编程开发网
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇for 循环变量作用域的小坑 下一篇Go基础系列:struct和嵌套struct

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容:

array(4) { ["type"]=> int(8) ["message"]=> string(24) "Undefined variable: jobs" ["file"]=> string(32) "/mnt/wp/cppentry/do/bencandy.php" ["line"]=> int(214) }