Giải thích về giao diện trống rỗng

Giao diện, giao diện {}, chuyển đổi

Một giao diện trong Go chỉ định mộtbộ phương pháp. Bất kỳ loại nào triển khai bộ phương pháp này được cho làtriển khai giao diện.

Một giao diện được triển khai không đồng nhất. Không códụng cụtừ khóa, được sử dụng trong các ngôn ngữ khác. Nếu một kiểu xác định toàn bộ tập phương thức của một giao diện, nó sẽ thực hiện nó. Nó được gọi là nhập cấu trúc, thời gian biên dịch tương đương vớigõ vịt.

Nếu nó đi như vịt, bơi như vịt và lang thang như vịt, đó là vịt

Quan trọng: trong Go, giao diện tóm tắt một tập hợp các phương thức (hành động), không phải dữ liệu.

Thí dụ

Ví dụ sau được lấy từCách sử dụng giao diện trong Gominh họa giao diện cơ bản của Động vật có thể Nói và các cấu trúc khác nhau đáp ứng giao diện:

type Animal interface {
    Speak() string
}

type Dog struct { }

func (d Dog) Speak() string { return “Woof!” }

type Cat struct { }

func (c Cat) Speak() string { return “Meow!” }

type Llama struct { }

func (l Llama) Speak() string { return “???” }

type JavaProgrammer struct { }

func (j JavaProgrammer) Speak() string { return “Design patterns!” }

Bạn có thể chế tạo [] Động vật và chạySpeak()về nội dung của nó:

func main() {
    animals := []Animal{Dog{}, Cat{}, Llama{}, JavaProgrammer{}}
    for _, animal := range animals {
        fmt.Println(animal.Speak())
    }
}

chơi

Giao diện trống

CácAnimalgiao diện nhỏ và đẹp, nhưng có một thứ thậm chí còn đơn giản hơn, nhưng có một khái niệm phức tạp để hiểu cho người mới bắt đầu: giao diện trống.

interface{}là giao diện trống của Go, một khái niệm chính. Mọi loại thực hiện nó theo định nghĩa.

Giao diện là một kiểu, vì vậy bạn có thể xác định ví dụ:

type Dog struct {
    Age interface{}
}

và bạn cũng có thể chuyển kiểu giao diện trống làm tham số hàm:

func Eat(t interface{}) {
    // ...
}

Chấp nhậninterface{}không có nghĩa là hàm chấp nhận bất kỳ kiểu nào, mà có nghĩa là Eat chấp nhận một giá trịinterface{}kiểu.

Trong thời gian chạy, Go sẽ chuyển đổi giá trị thực tế được chuyển thànhinterface{}giá trị.

Nếu bạn xác định một trường trong cấu trúc với kiểuinterface{}, bạn có thể gán cho nó một giá trị thuộc bất kỳ loại nào. Ví dụ:

package main

import “fmt”

type Dog struct { Age interface{} }

func main() { dog := Dog{} dog.Age = “3” fmt.Printf("%#v %T\n", dog.Age, dog.Age)

<span style="color:#a6e22e">dog</span>.<span style="color:#a6e22e">Age</span> = <span style="color:#ae81ff">3</span>
<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">"%#v %T\n"</span>, <span style="color:#a6e22e">dog</span>.<span style="color:#a6e22e">Age</span>, <span style="color:#a6e22e">dog</span>.<span style="color:#a6e22e">Age</span>)

<span style="color:#a6e22e">dog</span>.<span style="color:#a6e22e">Age</span> = <span style="color:#e6db74">"not really an age"</span>
<span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">"%#v %T"</span>, <span style="color:#a6e22e">dog</span>.<span style="color:#a6e22e">Age</span>, <span style="color:#a6e22e">dog</span>.<span style="color:#a6e22e">Age</span>)

}

chơi

bản in

"3" string
3 int
"not really an age" string

Tất nhiên điều này không giới hạn ở các loại cơ bản: bạn có thể lưu trữ một lát cắt, một bản đồ, bất kỳ cấu trúc tùy chỉnh nào trong đó.

Cách giao diện được thể hiện bên trong

Nội bộ, một giao diệngiá trịlà haiwords, tập hợp các byte.

Một từ trỏ đến loại giá trị cơ bản. Một từ trỏ đến dữ liệu.

Chuyển đổi

Khi chúng tôi đã làmanimals := []Animal{Dog{}, Cat{}, Llama{}, JavaProgrammer{}}trước đây, Go đã tự động chuyển đổi tất cả các loại động vật cụ thể của chúng tôi thành loại Động vật. Mọi phần tử của lát cắt đó bây giờ là kiểu Động vật, với kiểu cơ bản trỏ đến các loài cụ thể, như Chó, Mèo và Llama.

Nhưng nếu một phương thức chấp nhận[]interface{}ví dụ, chúng ta không thể chỉ vượt quaanimalsvới nó, bởi vì loại không phù hợp. Chúng ta cần phảichuyển đổi rõ ràng từngAnimalđến mộtinterface{}trước đó, với một vòng lặp, như được mô tả trongTôi có thể chuyển đổi [] T sang [] giao diện {} khôngCâu hỏi thường gặp:

t := []int{1, 2, 3, 4}
s := make([]interface{}, len(t))
for i, v := range t {
    s[i] = v
}

Xác định loại cơ bản của mộtinterface{}

Nếu một giá trị thuộc loạiinterface{}, bạn có thể muốn xác định loại cơ bản. Làm sao? Khi bật công tắc.(type), giốngLượt đi hiệu quảgiải thích:

Một công tắc cũng có thể được sử dụng để khám phá kiểu động của một biến giao diện. Việc chuyển kiểu như vậy sử dụng cú pháp của xác nhận kiểu với kiểu từ khóa bên trong dấu ngoặc đơn. Nếu switch khai báo một biến trong biểu thức, biến đó sẽ có kiểu tương ứng trong mỗi mệnh đề. Việc sử dụng lại tên trong những trường hợp như vậy cũng rất dễ hiểu, trong trường hợp đó, khai báo một biến mới có cùng tên nhưng khác kiểu trong mỗi trường hợp.

var t interface{}
t = functionOfSomeType()
switch t := t.(type) {
default:
    fmt.Printf("unexpected type %T\n", t)     // %T prints whatever type t has
case bool:
    fmt.Printf("boolean %t\n", t)             // t has type bool
case int:
    fmt.Printf("integer %d\n", t)             // t has type int
case *bool:
    fmt.Printf("pointer to boolean %t\n", *t) // t has type *bool
case *int:
    fmt.Printf("pointer to integer %d\n", *t) // t has type *int
}

Các hướng dẫn về go khác: