类属性和方法的可见性
前面我们已经陆续介绍了 Go 语言中面向对象的基本特性,包括自定义类的实现、构造函数、成员方法、类的继承、方法重写等,今天我们来系统介绍下类的属性和成员方法的可见性。
在 Go 语言中,没有类似 PHP 和 Java 那种命名空间的概念,不过 Go 语言也是通过包来管理源代码的,包往往与文件系统的目录结构存在映射关系,Go 语言在寻找变量、函数、类属性及方法的时候,会先查看 GOPATH
这个系统环境变量,然后根据该变量配置的路径列表依次去对应路径下的 src
目录下根据包名查找对应的目录,如果对应目录存在,则再到该目录下查找对应的变量、函数、类属性和方法,因此我们可以把归属于同一个目录的文件看作归属于同一个包,归属同一个包的代码具备以下特性:
- 归属于同一个包的代码包声明语句要一致,即同一级目录的源文件必须属于同一个包;
- 在同一个包下不同的不同文件中不能重复声明同一个变量、函数和类;
另外,需要注意的是 main
函数作为程序的入口函数,只能存在于 main
包中,main
包通常对应 src
目录,但也可以将其它子目录声明为 main
包。
Go 语言中,无论是变量、函数还是类属性及方法,它们的可见性都是与包相关联的,而不是类似传统面向编程那样,类属性和方法的可见性封装在对应的类中,然后通过 private
、protected
和 public
这些关键字来描述其可见性,Go 语言没有这些关键字,和变量和函数一样,对应 Go 语言的自定义类来说,属性和方法的可见性根据其首字母大小写来决定,如果属性名或方法名首字母大写,则可以在其他包中直接访问这些属性和方法,否则只能在包内访问,所以 Go 语言中的可见性都是包一级的,而不是类一级的。
下面我们根据上面介绍的包特性及可见性将前面编写的 Animal
、Dog
类放到 src
目录下的 animal
包中,然后在 src
目录下的 oop.go
文件中调用这两个类。
首先我们在 src
目录下创建一个 animal
子目录,然后在这个子目录下创建源文件 animal.go
用于保存 Animal
类代码:
package animal
type Animal struct {
name string
}
func NewAnimal(name string) *Animal {
return &Animal{name:name}
}
func (a Animal) Call() string {
return "动物的叫声..."
}
func (a Animal) FavorFood() string {
return "爱吃的食物..."
}
func (a Animal) GetName() string {
return a.name
}
我们在这个类中新增了 NewAnimal
方法作为 Animal
的构造函数,由于构造函数肯定需要在包以外的地方调用,所以将其首字母大写了,Animal
类包含了 name
属性,首字母小写表示只在 animal
包内可见,然后我们在同一个目录下创建 dog.go
用于保存 继承了 Animal
类的 Dog
类源码:
package animal
type Dog struct {
*Animal
}
func (d Dog) FavorFood() string {
return "骨头"
}
func (d Dog) Call() string {
return "汪汪汪"
}
在 Dog
类中,我们定义了一个 *Animal
类型的匿名属性,表示它继承了 *Animal
的所有成员方法,然后我们在 Dog
类中扩展了父类的 FavorFood
和 Call
成员方法。
最后,我们在 src
目录下创建一个 oop.go
文件来编写调用代码:
package main
import (
"animal"
"fmt"
)
func main() {
ani := animal.NewAnimal("狗")
dog := animal.Dog{ani}
fmt.Println(dog.GetName(), dog.Call(), dog.FavorFood())
}
由于 Animal
和 Dog
类都是定义在 animal
包中,所以需要通过 import
引入 animal
包,然后经过初始化后,就可以调用 dog
实例上的可见属性和方法了,在这里我们是无法调用 Animal
类的 name
属性的,因为该属性只在 animal
包内可见。同样,如果 GetName
、Call
或者 FavorFood
任意一个方法首字母小写,那么这里调用也会报错,提示找不到该属性字段或方法。
此外,我们还可以对引入的包设置别名,这在引入包的路径很长或者最后一级目录同名时适用,比如我们可以把上述 animal
包设置别名 animal2
:
package main
import (
animal2 "animal"
"fmt"
)
func main() {
ani := animal2.NewAnimal("狗")
dog := animal2.Dog{ani}
fmt.Println(dog.GetName(), dog.Call(), dog.FavorFood())
}
5 Comments
我这里报can't load package: package .: no Go files in E:\go,查看了环境GOPATH=E:\go,项目目录是E:\go\src, 是哪里配错了吗?
有没有和 Go moudles 混用
我按上面的操作,不同包下会报implicit assignment of unexported field 'name' in class.Animal literal
错误定位在这一行ani := animal.NewAnimal("狗")
但是放回跟mian同包下,会报undefined: Animal
错误定位在这一行ani := NewAnimal("狗")
倒数第三行这里是不是应该传指针才对? dog := animal2.Dog{&ani}
ani已经是指针了