目录

Go 类型系统

Go中,类的继承树并无意义,你只需要知道这个类实现了哪些方法,每个方法是啥含义就足够了。

  • 实现类的时候,只需要关心自己应该提供哪些方法,不用纠结接口要拆分地多细才合理。接口由使用方法,按需定义,而不用事前规划。

  • 接口由使用方按自身需求来定义,使用方无需关心是否有其他模块定义过类似的接口。

类型一致问题

https://juejin.im/post/5d5ff27d518825637965f3f3

怎样才算同一类型?

  • 基本类型:就是基本类型相同,比如int8 string complex128

  • Point:基本类型相同的指针是同一类型

  • array:长度相同 + 基本类型相同

  • slice:基本类型相同

  • map:键和值类型相同

  • struct:相同字段、类型、tag + 相同顺序

  • func:相同签名(参数列表、返回值列表)

  • interface:相同方法集

  • channel:基本类型相同 + 方向

Type自定义类型后是否和原来类型是同一类型

操作数ab同一类型意味着什么?

  • 可以进行=赋值运算

  • ==比较操作的前提条件

Slice类型的比较

禁止比较,原因有:

a := []interface{}{ 1, 2.0 }
a[1] = a // 会导致递归调用
fmt.Println(a)
// !!!
// runtime: goroutine stack exceeds 1000000000-byte limit
// fatal error: stack overflow
  • 切片如果直接比较引用地址,是不合适的。首先,切片与数组是比较相近的类型,比较方式的差异会造成使用者的混淆。另外,长度和容量是切片类型的一部分,不同长度和容量的切片如何比较?

  • 切片如果像数组那样比较里面的元素,又会出现上来提到的循环引用的问题。虽然可以在语言层面解决这个问题,但是 golang 团队认为不值得为此耗费精力。

Map类型的比较

因为Map的值可能是Slice,所以干脆将Map类型做成不可比较类型。

引用类型的比较

引用类型的比较实际判断的是两个变量是不是指向同一份数据,它不会去比较实际指向的数据。

type A struct {
    a int
    b string
}

aa := &A { a : 1, b : "test1" }
bb := &A { a : 1, b : "test1" }
cc := aa
fmt.Println(aa == bb) // false
fmt.Println(aa == cc) // true

Channel的比较

看是否是同一个Channel

ch1 := make(chan int, 1)
ch2 := make(chan int, 1)
ch3 := ch1

fmt.Println(ch1 == ch2)  // false
fmt.Println(ch1 == ch3)  // true

字符串

type stringStruct struct{
    str unsafe.Pointer // 存储空间的起始地址
    len int            // 字节数
} // 字符串的实现结构

字符串存储以utf8编码,存储的是unicode字符串,零值为""

支持!= == > < + += 操作符号,支持这样s[3]读取单个字节,支持切片语法s1 = s[1:4] s1切片底层引用的是s数组

range操作对字符串做了优化,默认是按字符取值,而不是按字节

要修改字符串内容,必须转成[]rune[]byte类型,完成后再转换回来。这种转换要重新分配内存,复制数据。

s := "Hello, 世界"
fmt.Println(len(s))                    // 13
fmt.Println(utf8.RuneCountInString(s)) // 9

for i := 0; i < len(s); {
    oneChar, size := utf8.DecodeRuneInString(s[i:])
    fmt.Printf("%d\t%c\n", i, oneChar) // 挨个打印字符
    i += size
}

// 对于 utf8 字符串, range 操作是会特殊处理的,每次都会返回单个正确的 utf8 字符
// i 是具体的偏移字节数
for i, oneChar := range s {
    fmt.Printf("%d\t%c\t%d\n", i, oneChar, oneChar)
}

r := []rune(s)
fmt.Println(len(r))          // 9
fmt.Println(r)               // [72 101 108 108 111 44 32 19990 30028]
fmt.Println(string(30028))   // 界
fmt.Println(string(1234566)) // � 不符合utf8规范的值,默认会转化成 \uFFFD 即 �

命名类型 与 未命名类型

具有同样结构的命名类型未命名类型的赋值:

type Person struct {
    name string
    age  int
}

type PersonCopy struct {
    name string
    age  int
}

func main() {
    link := struct {
        name string
        age  int
    }{
        "link",
        18,
    }

    var linkCopy Person
    var linkCopyCopy PersonCopy

    // 1. 命名类型 = 未命名类型 ok
    linkCopy = link

    // 2. 命名类型 = 命名类型
    //  cannot use linkCopy (type Person) as type PersonCopy in assignment
    linkCopyCopy = linkCopy

    // 3. 未命名类型 = 命名类型 ok
    linkCopyCopy.name = "linkCopyCopy"
    link = linkCopyCopy

    fmt.Println("link:", link, "\nlinkCopy:", linkCopy, "\nlinkCopyCopy:", linkCopyCopy)
}

通道的赋值:

a := make(chan int, 2) // 双向通道 转 单向通道, b 为未命名类型
fmt.Printf("%#v\n", a) // (chan int)(0xc0000c2000)

var b chan<- int = a
fmt.Printf("%#v\n", b) // (chan<- int)(0xc0000c2000)

b <- 2

常数Const是没有类型的

const (
        MaxInt64 = int(^uint64(0) >> 1)
        MinInt64 = -MaxInt64 - 1
        Big      = 1 << 100
        Small    = Big >> 99
)
func needInt(x int) int { return x*10 + 1 }
func needFloat(x float64) float64 {
    return x * 0.1
}
func main() {
    fmt.Println(needInt(Small))
    fmt.Println(needFloat(Small))
    fmt.Println(needFloat(Big))
    fmt.Printf("Max int64 : %v\n ", MaxInt64)
    fmt.Printf("Min int64 : %v\n ", MinInt64)
}

type 与 type = 的区别

type Brand struct {
}
func (t Brand) Show() {
    fmt.Println("Brand Show")
}

type FakeBrand = Brand // 这里去掉 = 看看
func (t FakeBrand) Show2(){
    fmt.Println("FakerBrand Show")
}

type Vehicle struct {
    FakeBrand
    Brand
}

func main() {
    var a Vehicle
    a.Show2() // ambiguous selector a.Show2
    a.Show() // ambiguous selector a.Show
    a.Brand.Show2()
    a.FakeBrand.Show()
    ta := reflect.TypeOf(a)
    for i := 0; i < ta.NumField(); i++ {
        f := ta.Field(i)
        fmt.Printf("FieldName: %v, FieldType: %v\n", f.Name, f.Type.Name())
    }
}

nil标识符

nil 是一个预定义好的标识符

// nil 是 map、slice、pointer、channel、func、interface 的零值
var arr []int
var ptr *int
var m map[int]string
var c chan int
var fn func()
var iface interface{}
var p *struct{}

// 1. nil 与 nil 无法比较
// invalid operation: nil == nil (operator == not defined on nil)
// fmt.Println(nil==nil)

// 2. 两个相同类型的 nil 值也可能无法比较
//invalid operation: s1 == s2 (slice can only be compared to nil)
//var arr2 []int
//fmt.Printf(arr == arr2)

// 3. 不同类型的 nil 是不能比较的
//invalid operation: m == ptr (mismatched types map[int]string and *int)
//fmt.Printf(m == ptr)

// 4. 可以将 nil 与任意变量比较
fmt.Println(arr == nil) // true

// 5. 即便值是nil 但类型与占用的字节数是不一样的
fmt.Printf("%p %T %#v \n", nil, nil, nil) // %!p(<nil>) <nil> <nil> %!d(MISSING)
fmt.Printf("%p %T %#v %d \n", arr, arr, arr, unsafe.Sizeof(arr)) // 0x0 []int []int(nil) 24
fmt.Printf("%p %T %#v %d \n", ptr, ptr, ptr, unsafe.Sizeof(ptr)) // 0x0 *int (*int)(nil) 8
fmt.Printf("%p %T %#v %d \n",m,m,m,unsafe.Sizeof( m )) // 0x0 map[int]string map[int]string(nil) 8
fmt.Printf("%p %T %#v %d \n", p,p,p,unsafe.Sizeof(p)) // 0x0 *struct {} (*struct {})(nil) 8
fmt.Printf("%p %T %#v %d \n", c,c,c,unsafe.Sizeof(c)) // 0x0 chan int (chan int)(nil) 8
fmt.Printf("%p %T %#v %d \n", fn, fn, fn,unsafe.Sizeof(fn)) // 0x0 func() (func())(nil) 8
fmt.Printf("%p %T %#v %d \n", iface, iface, iface,unsafe.Sizeof(iface)) // %!p(<nil>) <nil> <nil> 16