struct用来自定义复杂数据结构,相当于其他面向对象语言中的Class。
语法:
type 标识符 struct{
field1 type
field2 type
}
如:
type Student struct{
Name string
Age int
Score int
}
var stu Student
var stu *Student = new(Student)
var stu *Student = &Student{}
其中,后两种返回的都是指向结构体的指针,访问形式如下:
stu.Name
、stu.Age
和stu.Score
或者 (*stu).Name
、(*stu).Age
、(*stu).Score
。
golang中的struct没有构造函数,一般可以使用工厂模式来解决这个问题。
package model
import "fmt"
type student struct {
name string
age int
}
func NewStudent(name string, age int) *student {
return &student{
name: name,
age: age,
}
}
func (s student)Print() {
fmt.Println(s.name, s.age)
}
package main
import (
"golang_learning/model"
"fmt"
"strings"
)
func main() {
s := model.NewStudent("sam", 18)
s.Print()
fmt.Println(strings.Repeat("*", 10))
}
我们可以为struct中的每个字段,写上一个tag。这个tag可以通过反射的机制获取到,最常用的场景就是json序列化和反序列化。
type student struct {
Name string "this is name field"
Age int "this is age field"
}
还有一种场景是,由于golang中的字段大写才能导出,到与其他语言交互时,字段名多为小写,这个时候也可以使用tag来解决。
type student struct {
Name string "name"
Age int "age"
}
type Wheel struct {
Name string
Size int
}
type Car struct {
Wheel
Comp string
Date time.Time
int
}
只要冲突的字段不再同一个级别,就不会panic;可以通过明确指定来访问。
type Wheel struct {
Name string
Size int
}
type Car struct {
Wheel
Comp string
Date time.Time
Size int
}
w := Wheel{"w", 1}
c := Car{w, "auto", time.Now(), 20}
fmt.Println("%v", w)
fmt.Println("%v", c)
fmt.Println(c.Size)
fmt.Println(c.Wheel.Size)
// 20
// 1
Golang中的方法是作用在特定类型的变量上,因此自定义类型,都可以有方法,而不仅仅是struct。如:
package main
import (
"fmt"
)
type Int int
func (i Int)print() {
fmt.Println(i)
}
func main() {
i := Int(99)
i.print()
}
// 99
定义:func (recevier type) methodName(参数列表)(返回值列表){}
另外,方法的访问控制,通过大小写控制。
方法和函数的区别:
结构体是值类型的,如果需要修改其内部的值,需要使用指针。
如果一个struct嵌套了另一个匿名结构体,那么这个结构可以直接访问匿名结构体的方法,从而实现了继承。
如果一个struct嵌套了多个匿名结构体,那么这个结构可以直接访问多个匿名结构体的方法,从而实现了多重继承。
如果一个struct嵌套了另一个有名结构体,那么这个模式就叫组合。
如果一个变量实现了String()这个方法,那么fmt.Println默认会调用这个
变量的String()进行输出。
package main
import (
"fmt"
"strconv"
)
type student struct{
name string
age int
}
func (s student)String() string{
return "name: " + s.name + ", age: " + strconv.Itoa(s.age)
}
func main() {
s := student{"lee", 18}
fmt.Println(s)
fmt.Printf("%s", s)
}
// name: lee, age: 18
// name: lee, age: 18
Interface类型可以定义一组方法,但是这些不需要实现。并且interface不能
包含任何变量。
interface类型默认是一个指针
type example interface{
Method1(参数列表) 返回值列表
Method2(参数列表) 返回值列表
...
}
var a example
a.Method1()
implement
类似的关键字一种事物的多种形态,都可以按照统一的接口进行操作
一个接口可以嵌套在另外的接口,比如:
type ReadWrite interface {
Read(b int) bool
Write(b int) bool
}
type Lock interface {
Lock()
Unlock()
}
type File interface {
ReadWrite
Lock
Close()
}
由于接口是一般类型,不知道其具体类型,如果要转换成具体类型,可以使用下面的方法进行转换:
var t int
var x interface{}
x = t
y, ok := x.(int) // 转成int
func classifier(items ...interface{}) {
for i, x := range items {
switch x.(type) {
case bool:
fmt.Printf("param #%d is a bool\n", i)
case float64:
fmt.Printf("param #%d is a float64\n", i)
case int, int64:
fmt.Printf("param #%d is an int\n", i)
case nil:
fmt.Printf("param #%d is nil\n", i)
case string:
fmt.Printf("param #%d is a string\n", i)
default:
fmt.Printf("param #%d’s type is unknown\n", i)
}
}
}
空接口没有任何方法,所以所有类型都实现了空接口。
var a int
var b interface{}
b = a
判断一个变量是否实现了指定接口:
type Stringer interface {
String() string
}
var v MyStruct
if sv, ok := v.(Stringer); ok {
fmt.Printf("v implements String():%s\n", sv.String())
}
变量slice和接口slice之间赋值操作
变量slice和接口slice之间赋值操作,应该使用for range
var a []int
var b []interface{}
// b = a //error!!!
for index, value := range a{
b[index] = value
}
fmt.Println(b)