Interface rỗng (any)
Thuộc phần Lập trình hướng đối tượng trong hành trình GO của Coddy. Bài học 28 trên 107.
Giao diện rỗng là giao diện không có phương thức (method) nào. Vì mọi kiểu dữ liệu (type) trong Go đều có ít nhất 0 phương thức, nên mọi kiểu dữ liệu đều tự động thỏa mãn giao diện rỗng. Nhờ vậy, giao diện rỗng có thể chứa giá trị thuộc bất kỳ kiểu dữ liệu nào.
var anything interface{}
anything = 42
anything = "hello"
anything = true
anything = []int{1, 2, 3}Từ Go 1.18, từ khóa any được giới thiệu làm bí danh cho interface{}. Hai cách viết này hoàn toàn có thể thay thế cho nhau, nhưng any dễ đọc hơn.
var value any = "Go is awesome"Giao diện rỗng hữu ích khi bạn cần xử lý các giá trị có kiểu chưa biết hoặc thay đổi. Các hàm như fmt.Println dùng giao diện này để nhận đối số thuộc bất kỳ kiểu nào:
func PrintAnything(v any) {
fmt.Println("Value:", v)
}
func main() {
PrintAnything(100)
PrintAnything("text")
PrintAnything(3.14)
}Tuy nhiên, điều này có một sự đánh đổi. Khi lưu một giá trị trong một interface rỗng, bạn sẽ mất quyền truy cập vào thông tin kiểu dữ liệu cụ thể của nó.
Bạn không thể gọi trực tiếp các phương thức hoặc truy cập các trường nếu chưa trích xuất kiểu dữ liệu bên dưới. Các bài học tiếp theo sẽ hướng dẫn cách lấy kiểu cụ thể từ một interface rỗng bằng cách dùng phép khẳng định kiểu và câu lệnh switch kiểu.
Thử thách
DễHãy tạo một vật chứa lưu trữ linh hoạt có thể chứa giá trị thuộc bất kỳ kiểu dữ liệu nào bằng cách dùng giao diện rỗng. Bạn sẽ tạo một chiếc hộp đơn giản để minh họa cách any cho phép bạn lưu trữ các kiểu dữ liệu khác nhau trong cùng một cấu trúc.
Bạn sẽ sắp xếp code trong hai tệp:
box.go: Định nghĩa một cấu trúcBoxvới một trường duy nhất làContent, có kiểuany. Tạo hàm khởi tạoNewBoxnhận một giá trị thuộc bất kỳ kiểu dữ liệu nào và trả về con trỏ đến một Box chứa giá trị đó. Thêm phương thứcDescribetrả về chuỗi cho biết nội dung được lưu trong hộp.main.go: Đọc các giá trị khác nhau từ đầu vào, tạo các hộp chứa một số nguyên, một chuỗi và một giá trị boolean, sau đó in nội dung mô tả của từng hộp để cho thấy cùng một kiểu Box có thể chứa các giá trị hoàn toàn khác nhau.
Đầu vào sẽ gồm:
- Dòng 1: Một giá trị số nguyên
- Dòng 2: Một giá trị chuỗi
- Dòng 3: Một giá trị boolean (
truehoặcfalse)
Phương thức Describe của bạn cần trả về một chuỗi theo định dạng sau:
Box contains: [value]Ví dụ, với các giá trị 42, hello và true, đầu ra của bạn sẽ là:
Box contains: 42
Box contains: hello
Box contains: trueĐiểm mấu chốt ở đây là cấu trúc Box duy nhất của bạn có thể lưu trữ một số nguyên, một chuỗi hoặc một giá trị boolean, tất cả là nhờ kiểu any chấp nhận giá trị thuộc bất kỳ kiểu dữ liệu nào. Mỗi hộp chứa một nội dung khác nhau, nhưng tất cả đều có cùng kiểu Box.
Tự làm thử
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Đọc giá trị số nguyên
line1, _ := reader.ReadString('\n')
intVal, _ := strconv.Atoi(strings.TrimSpace(line1))
// Đọc giá trị chuỗi
line2, _ := reader.ReadString('\n')
strVal := strings.TrimSpace(line2)
// Đọc giá trị boolean
line3, _ := reader.ReadString('\n')
boolVal, _ := strconv.ParseBool(strings.TrimSpace(line3))
// TODO: Tạo một hộp cho mỗi giá trị bằng NewBox
// TODO: In ra mô tả của từng hộp bằng phương thức Describe
// Dùng các biến để tránh lỗi biên dịch (xóa các dòng này khi triển khai)
_ = intVal
_ = strVal
_ = boolVal
// Ví dụ về định dạng đầu ra:
// fmt.Println(box.Describe())
}
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Nền tảng OOP trong Go
Tệp bên ngoàiWorkspace và module trong GoPackage và importTên được export và không được exportGiới thiệu về OOP trong GoStruct như lớpĐịnh nghĩa phương thức cho structReceiver kiểu con trỏ và kiểu giá trịKhởi tạo structHàm khởi tạoÔn tập: máy tính đơn giản4Interface
Giới thiệu về interfaceTriển khai ngầm địnhInterface như một hợp đồngInterface rỗng (any)Khẳng định kiểuswitch kiểuKết hợp interfaceInterface Stringer và ErrorÔn tập: Máy tính hình học7Tính đóng gói
Trường được xuất và không được xuấtĐóng gói ở cấp packagePhương thức getter và setterẨn thông tin trong GoÔn tập: hồ sơ học sinh10Generics (Go 1.18 trở lên)
Giới thiệu về GenericsTham số kiểuRàng buộc kiểuStruct tổng quátGiải pháp thay thế cho phương thức tổng quátÔn tập: tập hợp tổng quát13Các mẫu thiết kế phần 1
Giới thiệu về mẫu thiết kếMẫu SingletonMẫu FactoryMẫu Abstract FactoryMẫu ObserverMẫu Strategy2Tìm hiểu chuyên sâu về kiểu dữ liệu và struct
Kiểu cơ bản và kiểu tổng hợpĐịnh nghĩa kiểu dữ liệu tùy chỉnhThẻ structStruct ẩn danhStruct lồng nhauGiá trị zero và giá trị mặc địnhÔn tập: sổ liên lạc5Ưu tiên kết hợp hơn kế thừa
Vì sao Go không có kế thừaNhập môn nhúng structTự động nâng phương thứcNhúng nhiều structNhúng và kết tậpChe khuất phương thức được nhúngÔn tập: hệ phân cấp nhân viên8Xử lý lỗi và lập trình hướng đối tượng
Giao diện errorCác kiểu lỗi tùy chỉnhBọc lỗi với fmt.ErrorfLỗi sentinelerrors.Is() và errors.As()Panic, defer và recoverÔn tập: trình phân tích tệp3Con trỏ và bộ nhớ
Cơ bản về con trỏ trong GoCon trỏ đến structTruyền theo giá trị và tham chiếuHàm new()Thu gom rác trong GoÔn tập: xây dựng danh sách liên kết6Đa hình trong Go
Đa hình qua interfaceĐịnh kiểu vịt trong GoQuy tắc thỏa mãn interfaceCác tập hợp đa hìnhTiêm phụ thuộcÔn tập: bộ xử lý thanh toán9Lập trình đồng thời và lập trình hướng đối tượng
Cơ bản về goroutineChannel và giao tiếpChannel có bộ đệm và không có bộ đệmCâu lệnh selectsync.Mutex và sync.RWMutexsync.WaitGroupThiết kế struct an toàn luồngÔn tập: worker poolTự luyện tập: Trình biên dịch Go online