1.可见性

这是我个人最喜欢Go的地方:变量、结构体、方法、结构体字段的名称首字母大写就是公有的,包内外都可见;首字母小写,就是私有,仅包内可见。 因为这一点,结构体也不需要啰嗦的Get/Set方法了。

2. interface{}

这是一个写法非常奇怪的类型,类型名称就叫做 "interface{}" ,作用相当于Java中的Object类型,该类型的变量可以接收其他任何类型的数据。本质上,"interface{}"是一个空接口,任何类型其实都实现了空接口,当然就可以接收任何类型的数据了。为什么“任何类型都实现了空接口”?可以参看下面的第4点“鸭子类型”。

3. nil

很多Javaer看到nil会下意识的认为等价于null,但是nil不是Java中的null;nil不是Java中的null;nil不是Java中的null;

nil只能赋给 指针、channel、func、interface、map或slice类型的变量, 这当中不包括string,不包括我们的自定义类型。 所以如下的代码编译不会通过的:

type Student struct{
    Name string
}
  
func createStudent() Student {
    if ... {
        return Student{}
    }else{
        return nil               //编程者想表达的意思是不满足某些条件就返回一个空值,但是在Golang中压根不能通过编译
    }  
}
func String() string{
    return nil                      //一样不会编译通过
}

4. 空值、默认值

除了nil,Golang中根本没有空值的概念。除了指针、channel、func、interface、map类型的变量在声明阶段是nil之外,其他的类型的变量在声明之后都会有一个“非空”的初始值,就跟Java中int类型的变量有默认初始值0一样。下面的代码,是不会编译报错并且能够正确运行的,最后的输出是0。 因为在“var stu Student” 这里就算没有显式地赋值,但是Golang会给stu赋一个初始化的值,该值中Student的其他字段都赋给初始化值(数值类型是0,string是空字符串,指针、channel、func、interface、map是nil,其他自定义类是对应的初始化值),所以最后使用stu.Age是没有问题的。

type Student struct {
    Age int
}
func main() {
    var stu Student                //这里定义了一个Student类型的变量,没有赋值
    fmt.Println(stu.Age)           // 这里会输出0
         
}

5. 哈希表、管道、指针、函数、接口的初始化值是nil,切片的初始化值是长度容量都是0的空切片。

var s []int
var m map[string]string
  
len := len(s)       //没有任何问题,求切片长度,结果是0
cap := cap(s)       //没有任何问题,求切片容量,结果是0
  
first :=s[0]        //会报数组越界错误,不会报空指针
s = append(s,1)     //这个也没有任何问题
  
m["a"]="b"          //这个会报空指针,报空指针,报空指针......

6.Golang中的接口遵循的是“鸭子类型”

所谓“鸭子类型”,就是只要一个“东西”看起来像鸭子,走起路来像鸭子,那么就认为它是鸭子。放到Golang中就是只要一个类型具有某个接口的所有方法,那么他就实现了这个接口,不需要显地用类似"implements" 这种关键字来标识具体实现了哪个接口。放到Java中就是如果一个类拥有一个方法 public void close() throws IOException{},那么就算这个类实现java.io.Closeable接口,根本不需要在定义类的时候显式地 "implements java.io.Closeable"(当然Java是不支持这么做的,仅仅是用熟悉的Java举个例子说明一下“鸭子类型”)。

由此可以推断在Go中,任何一个数据类型都实现了一个空接口。实际上也是如此,Go中有一个自带的空接口类型" interface{} ",该接口类型的变量可以接收其他任何类型的赋值。

7.结构体的变种

如下代码:

type MyMap map[string]string
  
type MyString string
  
type MyFunc func(int,int) int
  
func (mf MyFunc) Add(a int,b int) int{
    return my(a,b)
}
  
type Math interface{
    Add(a int,b int) int
}

这是一种比较奇怪但是令人兴奋的定义新结构体方法,简单来看,新定义的结构体就是原有类型的别名,能够在原有类型上“点”出来的东西都能够在新结构体上“点”出来。通过这种方法,可以扩展原有类型的方法,比如上面的MyMap,它就是一个哈希表,那么"[ 键值 ]"这个操作完全可作用于MyMap上,并且还可以定义其他的方法作用于MyMap上。还有MyFunc结构体,它实现了Math接口,并且在实现方法里面直接调用了自己。