Refleksi
Refleksi adalah mekanisme untuk memeriksa struktur bahasa itu sendiri saat runtime, ia dapat sangat fleksibel dalam menghadapi beberapa masalah, tetapi kerugian yang dibawa juga sangat jelas, misalnya masalah performa dll. Di Go, refleksi terkait erat dengan interface{}, sebagian besar, di mana ada interface{} muncul, akan ada refleksi. API refleksi di Go disediakan oleh paket standar reflect.
Interface
Sebelum memulai, mari pahami dua interface di paket runtime. Di Go, interface pada dasarnya adalah struct, Go pada runtime membagi interface menjadi dua kategori besar, satu adalah interface tanpa method set, yang lain adalah interface dengan method set. Untuk interface yang berisi method set, pada runtime direpresentasikan oleh struct iface sebagai berikut
type iface struct {
tab *itab // berisi tipe data, tipe interface, method set dll
data unsafe.Pointer // pointer ke nilai
}Sedangkan untuk interface tanpa method set, pada runtime direpresentasikan oleh struct eface, sebagai berikut
type eface struct {
_type *_type // tipe
data unsafe.Pointer // pointer ke nilai
}Kedua struct ini di paket reflect memiliki tipe struct yang sesuai, iface sesuai dengan nonEmptyInterface
type nonEmptyInterface struct {
itab *struct {
ityp *rtype // tipe interface statis
typ *rtype // tipe konkret dinamis
hash uint32 // hash tipe
_ [4]byte
fun [100000]unsafe.Pointer // method set
}
word unsafe.Pointer // pointer ke nilai
}Sedangkan eface sesuai dengan emptyInterface
type emptyInterface struct {
typ *rtype // tipe konkret dinamis
word unsafe.Pointer // nilai pointer
}Untuk kedua tipe ini,官方 memberikan definisi yang sangat jelas
nonEmptyInterface: nonEmptyInterface is the header for an interface value with methodsemptyInterface: emptyInterface is the header for an interface{} value
Di atas disebutkan istilah tipe konkret dinamis,原文 adalah dynamic concrete type, pertama Go adalah bahasa tipe statis seratus persen, statis ini tercermin dalam tipe interface abstrak yang ditampilkan ke luar adalah tetap, sedangkan dinamis表示 adalah tipe konkret yang disimpan di底层 interface dapat berubah. Sampai sini, untuk prinsip sederhana interface hanya perlu memahami ini sudah cukup untuk memenuhi pembelajaran refleksi selanjutnya.
Jembatan
Di paket reflect, masing-masing ada tipe interface reflect.Type untuk merepresentasikan tipe di Go, tipe struct reflect.Value untuk merepresentasikan nilai di Go
type Type interface {
...
Name() string
PkgPath() string
Size() uintptr
String() string
Kind() Kind
...
}
type Value struct {
typ *rtype
ptr unsafe.Pointer
flag
}Kode di atas menghilangkan banyak detail, pertama hanya perlu memahami keberadaan kedua tipe ini. Semua operasi terkait refleksi di Go didasarkan pada kedua tipe ini, paket reflect menyediakan dua fungsi untuk mengkonversi tipe di Go menjadi kedua tipe di atas untuk melakukan operasi refleksi, masing-masing adalah fungsi reflect.TypeOf
func TypeOf(i any) TypeDan fungsi reflect.ValueOf
func ValueOf(i any) ValueDapat dilihat tipe parameter kedua fungsi adalah any, yaitu alias interface{}. Jika ingin melakukan operasi refleksi, perlu mengkonversi tipenya menjadi interface{} terlebih dahulu, ini juga mengapa di depan disebutkan只要有 refleksi tidak dapat lepas dari empty interface. Tidak ketat mengatakan, empty interface adalah jembatan yang menghubungkan sistem tipe Go dan refleksi, gambar di bawah ini sangat jelas menggambarkan prosesnya.

TIP
Di bawah ini untuk memudahkan, secara统一 menggunakan alias any untuk menggantikan interface{}
Inti
Di Go ada tiga hukum refleksi klasik, dikombinasikan dengan konten yang dibahas di atas sangat mudah dipahami, masing-masing sebagai berikut
Refleksi dapat mengkonversi variabel tipe
interface{}menjadi objek refleksiRefleksi dapat mengembalikan objek refleksi menjadi variabel tipe
interface{}Untuk memodifikasi objek refleksi, nilainya harus dapat diset
Ketiga hukum ini adalah inti refleksi Go, saat perlu mengakses informasi tipe terkait, perlu menggunakan reflect.TypeOf, saat perlu memodifikasi nilai refleksi, perlu menggunakan reflect.ValueOf
Tipe
reflect.Type merepresentasikan tipe di Go, menggunakan fungsi reflect.TypeOf() dapat mengkonversi variabel menjadi reflect.Type. Contoh kode sebagai berikut
func main() {
str := "hello world!"
reflectType := reflect.TypeOf(str)
fmt.Println(reflectType)
}Hasil output
stringKind
Untuk Type, Go internal menggunakan reflect.Kind untuk merepresentasikan tipe dasar di Go, pada dasarnya adalah unsigned integer uint.
type Kind uintPaket reflect menggunakan Kind untuk mengenumerasi semua tipe dasar di Go, sebagai berikut
const (
Invalid Kind = iota
Bool
Int
Int8
Int16
Int32
Int64
Uint
Uint8
Uint16
Uint32
Uint64
Uintptr
Float32
Float64
Complex64
Complex128
Array
Chan
Func
Interface
Map
Pointer
Slice
String
Struct
UnsafePointer
)Tipe Kind hanya mengimplementasikan method String() dari interface Stringer, tipe ini juga hanya memiliki satu method ini, nilai return method String() berasal dari slice internalnya, sebagai berikut, penulisan ini sekilas sangat mirip map, tetapi sebenarnya ini adalah penulisan khusus di Go: syntax inisialisasi index (index expressions in slice literals)
var kindNames = []string{
Invalid: "invalid",
Bool: "bool",
Int: "int",
Int8: "int8",
Int16: "int16",
Int32: "int32",
Int64: "int64",
Uint: "uint",
Uint8: "uint8",
Uint16: "uint16",
Uint32: "uint32",
Uint64: "uint64",
Uintptr: "uintptr",
Float32: "float32",
Float64: "float64",
Complex64: "complex64",
Complex128: "complex128",
Array: "array",
Chan: "chan",
Func: "func",
Interface: "interface",
Map: "map",
Pointer: "ptr",
Slice: "slice",
String: "string",
Struct: "struct",
UnsafePointer: "unsafe.Pointer",
}type Type interface{
Kind() Kind
}Melalui Kind, dapat mengetahui nilai yang disimpan empty interface sebenarnya adalah tipe dasar apa, misalnya
func main() {
// Deklarasikan variabel tipe any
var eface any
// Assign nilai
eface = 100
// Melalui method Kind, untuk mendapatkan tipenya
fmt.Println(reflect.TypeOf(eface).Kind())
}Hasil output
intElem
type Type interface{
Elem() Type
}Menggunakan method Type.Elem(), dapat判断 tipe elemen yang disimpan oleh struktur data dengan tipe any, tipe parameter底层 yang dapat diterima harus salah satu dari pointer, slice, array, channel, map, jika tidak akan panic. Berikut adalah contoh kode
func main() {
var eface any
eface = map[string]int{}
rType := reflect.TypeOf(eface)
// key() akan mengembalikan tipe refleksi key map
fmt.Println(rType.Key().Kind())
fmt.Println(rType.Elem().Kind())
}Output adalah
string
intPointer juga dapat dipahami sebagai container, untuk pointer menggunakan Elem() akan mendapatkan tipe refleksi elemen yang ditunjuknya, contoh kode sebagai berikut
func main() {
var eface any
// Assign pointer
eface = new(strings.Builder)
rType := reflect.TypeOf(eface)
// Mendapatkan tipe refleksi elemen yang ditunjuk pointer
vType := rType.Elem()
// Output path paket
fmt.Println(vType.PkgPath())
// Output namanya
fmt.Println(vType.Name())
}strings
BuilderUntuk array, slice, channel penggunaannya sama.
Size
type Type interface{
Size() uintptr
}Melalui method Size dapat mendapatkan ukuran byte yang ditempati tipe yang sesuai, contoh sebagai berikut
func main() {
fmt.Println(reflect.TypeOf(0).Size())
fmt.Println(reflect.TypeOf("").Size())
fmt.Println(reflect.TypeOf(complex(0, 0)).Size())
fmt.Println(reflect.TypeOf(0.1).Size())
fmt.Println(reflect.TypeOf([]string{}).Size())
}Hasil output adalah
8
16
16
8
24TIP
Menggunakan unsafe.Sizeof() dapat mencapai efek yang sama
Comparable
type Type interface{
Comparable() bool
}Melalui method Comparable dapat判断 apakah suatu tipe dapat dibandingkan, contoh sebagai berikut
func main() {
fmt.Println(reflect.TypeOf("hello world!").Comparable())
fmt.Println(reflect.TypeOf(1024).Comparable())
fmt.Println(reflect.TypeOf([]int{}).Comparable())
fmt.Println(reflect.TypeOf(struct{}{}).Comparable())
}Output sebagai berikut
true
true
false
trueImplements
type Type interface{
Implements(u Type) bool
}Melalui method Implements dapat判断 apakah suatu tipe mengimplementasi interface tertentu
type MyInterface interface {
My() string
}
type MyStruct struct {
}
func (m MyStruct) My() string {
return "my"
}
type HisStruct struct {
}
func (h HisStruct) String() string {
return "his"
}
func main() {
rIface := reflect.TypeOf(new(MyInterface)).Elem()
fmt.Println(reflect.TypeOf(new(MyStruct)).Elem().Implements(rIface))
fmt.Println(reflect.TypeOf(new(HisStruct)).Elem().Implements(rIface))
}Hasil output
true
falseConvertibleTo
type Type interface{
ConvertibleTo(u Type) bool
}Menggunakan method ConvertibleTo dapat判断 apakah suatu tipe dapat dikonversi menjadi tipe target yang ditentukan
type MyInterface interface {
My() string
}
type MyStruct struct {
}
func (m MyStruct) My() string {
return "my"
}
type HisStruct struct {
}
func (h HisStruct) String() string {
return "his"
}
func main() {
rIface := reflect.TypeOf(new(MyInterface)).Elem()
fmt.Println(reflect.TypeOf(new(MyStruct)).Elem().ConvertibleTo(rIface))
fmt.Println(reflect.TypeOf(new(HisStruct)).Elem().ConvertibleTo(rIface))
}Output
true
falseNilai
reflect.Value merepresentasikan nilai interface refleksi, menggunakan fungsi reflect.ValueOf() dapat mengkonversi variabel menjadi reflect.Value. Contoh kode sebagai berikut
func main() {
str := "hello world!"
reflectValue := reflect.ValueOf(str)
fmt.Println(reflectValue)
}Hasil output
hello world!Type
func (v Value) Type() TypeMethod Type dapat mendapatkan tipe dari nilai refleksi
func main() {
num := 114514
rValue := reflect.ValueOf(num)
fmt.Println(rValue.Type())
}Output
intElem
func (v Value) Elem() ValueMendapatkan nilai refleksi elemen dari nilai refleksi
func main() {
num := new(int)
*num = 114514
// Dengan pointer sebagai contoh
rValue := reflect.ValueOf(num).Elem()
fmt.Println(rValue.Interface())
}Output
114514Pointer
Ada dua cara untuk mendapatkan pointer dari nilai refleksi
// Mengembalikan nilai refleksi pointer yang merepresentasikan alamat v
func (v Value) Addr() Value
// Mengembalikan uintptr ke nilai asli v setara dengan uintptr(Value.Addr().UnsafePointer())
func (v Value) UnsafeAddr() uintptr
// Mengembalikan uintptr ke nilai asli v
// Hanya ketika Kind v adalah Chan, Func, Map, Pointer, Slice, UnsafePointer, jika tidak akan panic
func (v Value) Pointer() uintptr
// Mengembalikan unsafe.Pointer ke nilai asli v
// Hanya ketika Kind v adalah Chan, Func, Map, Pointer, Slice, UnsafePointer, jika tidak akan panic
func (v Value) UnsafePointer() unsafe.PointerContoh sebagai berikut
func main() {
num := 1024
ele := reflect.ValueOf(&num).Elem()
fmt.Println("&num", &num)
fmt.Println("Addr", ele.Addr())
fmt.Println("UnsafeAddr", unsafe.Pointer(ele.UnsafeAddr()))
fmt.Println("Pointer", unsafe.Pointer(ele.Addr().Pointer()))
fmt.Println("UnsafePointer", ele.Addr().UnsafePointer())
}Output
&num 0xc0000a6058
Addr 0xc0000a6058
UnsafeAddr 0xc0000a6058
Pointer 0xc0000a6058
UnsafePointer 0xc0000a6058TIP
fmt.Println akan refleksi mendapatkan tipe parameter, jika tipe reflect.Value, akan otomatis memanggil Value.Interface() untuk mendapatkan nilai aslinya.
Ganti dengan map dan coba lagi
func main() {
dic := map[string]int{}
ele := reflect.ValueOf(&dic).Elem()
println(dic)
fmt.Println("Addr", ele.Addr())
fmt.Println("UnsafeAddr", *(*unsafe.Pointer)(unsafe.Pointer(ele.UnsafeAddr())))
fmt.Println("Pointer", unsafe.Pointer(ele.Pointer()))
fmt.Println("UnsafePointer", ele.UnsafePointer())
}Output
0xc00010e4b0
Addr &map[]
UnsafeAddr 0xc00010e4b0
Pointer 0xc00010e4b0
UnsafePointer 0xc00010e4b0Set Nilai
func (v Value) Set(x Value)Jika ingin memodifikasi nilai refleksi melalui refleksi, maka nilainya harus dapat di-address, saat ini harus melalui pointer untuk memodifikasi nilai elemennya, bukan langsung mencoba memodifikasi nilai elemen.
func main() {
// *int
num := new(int)
*num = 114514
rValue := reflect.ValueOf(num)
// Mendapatkan elemen yang ditunjuk pointer
ele := rValue.Elem()
fmt.Println(ele.Interface())
ele.SetInt(11)
fmt.Println(ele.Interface())
}Output sebagai berikut
114514
11Mendapatkan Nilai
func (v Value) Interface() (i any)Melalui method Interface() dapat mendapatkan nilai asli dari nilai refleksi
func main() {
var str string
str = "hello"
rValue := reflect.ValueOf(str)
if v, ok := rValue.Interface().(string); ok {
fmt.Println(v)
}
}Output
helloFungsi
Melalui refleksi dapat mendapatkan semua informasi fungsi, juga dapat memanggil fungsi melalui refleksi
Informasi
Melalui tipe refleksi untuk mendapatkan semua informasi fungsi
func Max(a, b int) int {
if a > b {
return a
}
return b
}
func main() {
rType := reflect.TypeOf(Max)
// Output nama fungsi, tipe fungsi literal tidak memiliki nama
fmt.Println(rType.Name())
// Output jumlah parameter, nilai return
fmt.Println(rType.NumIn(), rType.NumOut())
rParamType := rType.In(0)
// Output tipe parameter pertama
fmt.Println(rParamType.Kind())
rResType := rType.Out(0)
// Output tipe nilai return pertama
fmt.Println(rResType.Kind())
}Output
2 1
int
intMemanggil
Melalui nilai refleksi untuk memanggil fungsi
func (v Value) Call(in []Value) []Valuefunc main() {
// Mendapatkan nilai refleksi fungsi
rType := reflect.ValueOf(Max)
//传入 array parameter
rResValue := rType.Call([]reflect.Value{reflect.ValueOf(18), reflect.ValueOf(50)})
for _, value := range rResValue {
fmt.Println(value.Interface())
}
}Output
50Struct
Misalkan ada struct sebagai berikut
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Address string `json:"address"`
money int
}
func (p Person) Talk(msg string) string {
return msg
}Mengakses Field
Struktur reflect.StructField sebagai berikut
type StructField struct {
// Nama field
Name string
// Nama paket
PkgPath string
// Nama tipe
Type Type
// Tag
Tag StructTag
// Offset byte field
Offset uintptr
// Index
Index []int
// Apakah field embedded
Anonymous bool
}Ada dua cara untuk mengakses field struct, satu melalui index, yang lain melalui nama.
type Type interface{
Field(i int) StructField
}Contoh mengakses melalui index sebagai berikut
func main() {
rType := reflect.TypeOf(new(Person)).Elem()
// Output jumlah field struct
fmt.Println(rType.NumField())
for i := 0; i < rType.NumField(); i++ {
structField := rType.Field(i)
fmt.Println(structField.Index, structField.Name, structField.Type, structField.Offset, structField.IsExported())
}
}Output
4
[0] Name string 0 true
[1] Age int 16 true
[2] Address string 24 true
[3] money int 40 falsetype Type interface{
FieldByName(name string) (StructField, bool)
}Contoh mengakses melalui nama sebagai berikut
func main() {
rType := reflect.TypeOf(new(Person)).Elem()
// Output jumlah field struct
fmt.Println(rType.NumField())
if field, ok := rType.FieldByName("money"); ok {
fmt.Println(field.Name, field.Type, field.IsExported())
}
}Output
4
money int falseMemodifikasi Field
Jika ingin memodifikasi nilai field struct, harus传入 pointer struct, berikut adalah contoh memodifikasi field
func main() {
//传入 pointer
rValue := reflect.ValueOf(&Person{
Name: "",
Age: 0,
Address: "",
money: 0,
}).Elem()
// Mendapatkan field
name := rValue.FieldByName("Name")
// Memodifikasi nilai field
if (name != reflect.Value{}) { // Jika mengembalikan reflect.Value{}, berarti field tidak ada
name.SetString("jack")
}
// Output struct
fmt.Println(rValue.Interface())
}Output
{jack 0 0}Untuk memodifikasi field private struct, perlu melakukan beberapa operasi tambahan, sebagai berikut
func main() {
//传入 pointer
rValue := reflect.ValueOf(&Person{
Name: "",
Age: 0,
Address: "",
money: 0,
}).Elem()
// Mendapatkan field private
money := rValue.FieldByName("money")
// Memodifikasi nilai field
if (money != reflect.Value{}) {
// Membangun nilai refleksi pointer ke field yang tidak diekspor struct ini
p := reflect.NewAt(money.Type(), money.Addr().UnsafePointer())
// Mendapatkan elemen yang ditunjuk pointer ini, yaitu field yang akan dimodifikasi
field := p.Elem()
// Memodifikasi nilai
field.SetInt(164)
}
// Output struct
fmt.Printf("%+v\n", rValue.Interface())
}Mengakses Tag
Setelah mendapatkan StructField, dapat langsung mengakses Tag-nya
// Jika tidak ada, ok adalah false
func (tag StructTag) Lookup(key string) (value string, ok bool)
// Jika tidak ada, mengembalikan string kosong
func (tag StructTag) Get(key string) stringContoh sebagai berikut
func main() {
rType := reflect.TypeOf(new(Person)).Elem()
name, ok := rType.FieldByName("Name")
if ok {
fmt.Println(name.Tag.Lookup("json"))
fmt.Println(name.Tag.Get("json"))
}
}Output
name true
nameMengakses Method
Mengakses method sangat mirip dengan proses mengakses field, hanya signature fungsi sedikit berbeda. Struktur reflect.Method sebagai berikut
type Method struct {
// Nama method
Name string
// Nama paket
PkgPath string
// Tipe method
Type Type
// Fungsi yang sesuai method, parameter pertama adalah receiver
Func Value
// Index
Index int
}Contoh mengakses informasi method sebagai berikut
func main() {
// Mendapatkan tipe refleksi struct
rType := reflect.TypeOf(new(Person)).Elem()
// Output jumlah method
fmt.Println(rType.NumMethod())
// Traversing output informasi method
for i := 0; i < rType.NumMethod(); i++ {
method := rType.Method(i)
fmt.Println(method.Index, method.Name, method.Type, method.IsExported())
}
}Output
1
0 Talk func(main.Person, string) string trueJika ingin mendapatkan detail parameter dan nilai return method, dapat melalui Method.Func untuk mendapatkan, prosesnya sama dengan mengakses informasi fungsi, modifikasi sedikit kode di atas
func main() {
// Mendapatkan tipe refleksi struct
rType := reflect.TypeOf(new(Person)).Elem()
// Output jumlah method
fmt.Println(rType.NumMethod())
// Traversing output informasi method
for i := 0; i < rType.NumMethod(); i++ {
method := rType.Method(i)
fmt.Println(method.Index, method.Name, method.Type, method.IsExported())
fmt.Println("Parameter method")
for i := 0; i < method.Func.Type().NumIn(); i++ {
fmt.Println(method.Func.Type().In(i).String())
}
fmt.Println("Nilai return method")
for i := 0; i < method.Func.Type().NumOut(); i++ {
fmt.Println(method.Func.Type().Out(i).String())
}
}
}Dapat dilihat parameter pertama adalah main.Person, yaitu tipe receiver
1
0 Talk func(main.Person, string) string true
Parameter method
main.Person
string
Nilai return method
stringMemanggil Method
Memanggil method sangat mirip dengan proses memanggil fungsi, dan tidak perlu传入 receiver secara manual, contoh sebagai berikut
func main() {
// Mendapatkan tipe refleksi struct
rValue := reflect.ValueOf(new(Person)).Elem()
// Output jumlah method
fmt.Println(rValue.NumMethod())
// Traversing output informasi method
talk := rValue.MethodByName("Talk")
if (talk != reflect.Value{}) {
// Memanggil method, dan mendapatkan nilai return
res := talk.Call([]reflect.Value{reflect.ValueOf("hello,reflect!")})
// Traversing output nilai return
for _, re := range res {
fmt.Println(re.Interface())
}
}
}Output
1
hello,reflect!Membuat
Melalui refleksi dapat membangun nilai baru, paket reflect sekaligus menyediakan fungsi yang lebih mudah digunakan berdasarkan beberapa tipe khusus.
Tipe Dasar
// Mengembalikan nilai refleksi pointer ke nilai refleksi
func New(typ Type) ValueDengan string sebagai contoh
func main() {
rValue := reflect.New(reflect.TypeOf(*new(string)))
rValue.Elem().SetString("hello world!")
fmt.Println(rValue.Elem().Interface())
}hello world!Struct
Pembuatan struct juga menggunakan fungsi reflect.New
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Address string `json:"address"`
money int
}
func (p Person) Talk(msg string) string {
return msg
}
func main() {
// Membuat nilai refleksi struct
rType := reflect.TypeOf(new(Person)).Elem()
person := reflect.New(rType).Elem()
fmt.Println(person.Interface())
}Output
{ 0 0}Slice
Refleksi membuat slice
func MakeSlice(typ Type, len, cap int) Valuefunc main() {
// Membuat nilai refleksi slice
rValue := reflect.MakeSlice(reflect.TypeOf(*new([]int)), 10, 10)
// Traversing assign nilai
for i := 0; i < 10; i++ {
rValue.Index(i).SetInt(int64(i))
}
fmt.Println(rValue.Interface())
}[0 1 2 3 4 5 6 7 8 9]Map
Refleksi membuat Map
func MakeMapWithSize(typ Type, n int) Valuefunc main() {
//Membangun nilai refleksi map
rValue := reflect.MakeMapWithSize(reflect.TypeOf(*new(map[string]int)), 10)
// Set nilai
rValue.SetMapIndex(reflect.ValueOf("a"), reflect.ValueOf(1))
fmt.Println(rValue.Interface())
}map[a:1]Channel
Refleksi membuat channel
func MakeChan(typ Type, buffer int) Valuefunc main() {
// Membuat nilai refleksi channel
makeChan := reflect.MakeChan(reflect.TypeOf(new(chan int)).Elem(), 0)
fmt.Println(makeChan.Interface())
}Fungsi
Refleksi membuat fungsi
func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Valuefunc main() {
//传入 tipe wrapper dan body fungsi
fn := reflect.MakeFunc(reflect.TypeOf(new(func(int))).Elem(), func(args []reflect.Value) (results []reflect.Value) {
for _, arg := range args {
fmt.Println(arg.Interface())
}
return nil
})
fmt.Println(fn.Type())
fn.Call([]reflect.Value{reflect.ValueOf(1024)})
}Output
func(int)
1024Sama Sepenuhnya
reflect.DeepEqual adalah fungsi yang disediakan di bawah paket refleksi untuk判断 apakah dua variabel sama sepenuhnya, signature sebagai berikut.
func DeepEqual(x, y any) boolFungsi ini melakukan penanganan untuk setiap tipe dasar, berikut adalah beberapa cara判断 tipe.
- Array: setiap elemen di array sama sepenuhnya
- Slice: keduanya
nil, dianggap sama sepenuhnya, atau keduanya tidak kosong, elemen dalam rentang panjang sama sepenuhnya - Struct: semua field sama sepenuhnya
- Map: keduanya
nil, adalah sama sepenuhnya, keduanya tidaknil, nilai yang dipetakan setiap key sama sepenuhnya - Pointer: menunjuk elemen yang sama atau elemen yang ditunjuk sama sepenuhnya
- Interface: saat tipe konkret interface sama sepenuhnya
- Fungsi: hanya ketika keduanya
niladalah sama sepenuhnya, jika tidak bukan sama sepenuhnya
Berikut adalah beberapa contoh:
Slice
func main() {
a := make([]int, 100)
b := make([]int, 100)
fmt.Println(reflect.DeepEqual(a, b))
}Output
trueStruct
func main() {
mike := Person{
Name: "mike",
Age: 39,
Father: nil,
}
jack := Person{
Name: "jack",
Age: 18,
Father: &mike,
}
tom := Person{
Name: "tom",
Age: 18,
Father: &mike,
}
fmt.Println(reflect.DeepEqual(mike, jack))
fmt.Println(reflect.DeepEqual(tom, jack))
fmt.Println(reflect.DeepEqual(jack, jack))
}Output
false
false
true