Swift Generics Part I

Theo tài liệu: Tutorial Generic programming là 1 cách viết functions và kiểu dữ liệu trong khi đưa ra những giả định tối thiểu về loại của dữ liệu sử dụng. Code viết theo kiểu swift generics sẽ ko cần đòi hỏi cụ thể nào về dữ liệu, cho phép code trở nên linh hoạt, dễ sử dụng lại và tạo ra code đẹp hơn, sạch hơn, ít lỗi hơn. Bạn sẽ thấy generic được sử dụng rất nhiều trong swift, mà cụ thể hay gặp nhất đó là Optional type. bạn có thể có optional của bất kỳ kiểu dữ liệu nào bạn muốn, kể cả kiểu dữ liệu bạn tự định nghĩa. kiểu dữ liệu optional được tạo ra theo kiểu generic theo dữ liệu nó cần chứa. Trong bài turorial này, chúng ta sẽ cùng xem xét các vấn đề sau:

  • Generics là gì
  • Sự hữu ích của Generics
  • Cách viết generic functions và data strutures
  • Cách sử dụng type constraints
  • Cách extend generic types

Getting Started

Tạo 1 file playground mới và viết đoạn code về function cộng 2 số nguyên như sau:

func addInts(x: Int, y: Int) -> Int {
 return x + y
}

Nếu bạn cần 1 hàm nữa để cộng các số thực, bạn lại phải viết thêm:

func addDoubles(x: Double, y: Double) -> Double {
 return x + y
}

let doubleSum = addDoubles(x: 1.0, y: 2.0)

function signatiures (kiểu trả về và kiểu tham số đầu vào) của addIntsaddDoubles là khác nhau, nhưng bodies là giống hệt nhau. Như vậy không chỉ việc có tới 2 function cho 1 việc là cộng 2 số, mà bạn còn bị lặp code. Generics có thể được sử dụng để biến 2 function thành 1 và loại bỏ code lặp.

Other Examples of Swift Generics

Có thể bạn không để ý, nhưng hầu hết các structures phổ biến mà bạn sử dụng đều là options: arrays, dictionaries và optionals,..

Arrays

Thêm đoạn code sau vào playground:

let numbers = [1, 2, 3]
let firstNumber = numbers[0]

Ở đây bạn tạo ra 1 array với 3 số và lấy số đầu tiên ở trong array đó, tiếp theo option-click vào numbers rồi sau đó là firstNumber bạn sẽ thấy:

Bởi vì swift có type inference (sự tự suy luận kiểu dữ liệu) bạn không cần phải định nghĩa rõ ràng kiểu dữ liệu của các hằng số (constants), nhưng chúng luôn có kiểu chính xác: numbers là kiểu [int]firstNumberInt. Array của swift là 1 kiểu generic type. Generic types yêu cầu 1 tham số chỉ ra kiểu của dữ liệu để có thể xác định được chính xác. Khi bạn tạo ra 1 instance, bạn phải xác định luôn cả type parameter của nó để instance có 1 kiểu cụ thể. Ở ví dụ trên, bởi vì type inferencetype safety của Swift mà array number chỉ có thể chứa giá trị kiểu Int. Bạn sẽ thấy bản chất của Array rõ ràng hơn bằng những dòng code sau, vẫn là 1 mảng kiểu Int:

var numbersAgain: Array<Int> = []
numbersAgain.append(1)
numbersAgain.append(2)
numbersAgain.append(3)

let firstNumberAgain = numbersAgain[0]

Bạn hãy check kiểu của numbersAgainfirstNumberAgain và bạn sẽ thấy kiểu của chúng vẫn y hệt như phía trên. Ở đây bạn đã chỉ ra kiểu của numbersAgain bằng cách sử dụng explicit generic syntax khi để <Int> đi ngay sau Array Hãy thử appending thứ gì khác ko phải là số nguyên vào array, như String:

numbersAgain.append("All hail Lord Farquaad")

Bạn sẽ nhận được error Cannot convert value of type 'String' to expected argument type 'Int'. Compiler đang nói rằng bạn ko thể thêm 1 chuỗi vào trong 1 mảng số nguyên. Bởi vì append là 1 method của generic type Array nên nó được gọi là generic method. Nó sẽ biết kiểu của các thành phần bên trong array và sẽ không để bạn thêm những thứ ko cùng kiểu vào đó.

Dictionaries

Dictionaries are also generic types and result in type-safe data structures. Tạo 1 dictionary như code dưới đây:

let countryCodes = ["Arendelle": "AR", "Genovia": "GN", "Freedonia": "FD"]
let countryCode = countryCodes["Freedonia"]

Check kiểu của cả countryCodescontryCode,. bạn sẽ thấy countryCodes là 1 dictionary với keys là kiểu String và giá trị cũng là kiểu String. Thử add thêm 1 item nữa vào trong countryCodes như sau:

countryCodes["Dicaverio"] = 18

Bạn sẽ thấy build lỗi như sau: cannot assign value of type 'Int' to type 'String?'

Optionals

Ở ví dụ phía trên, bạn sẽ nhớ rằng kiểu value trong contryCodesString?, mà là viết tắt cho:

Optional<String>

Bạn sẽ thấy rằng 2 ký tự < > chính là generic mà ta đã nói ở trên như khi khai báo rõ ràng Array. Ở đây, compiler đã bắt buộc bạn chỉ có thể truy cập dictionary với key là String và luôn luôn nhận về value là String. Một optional type được sử dụng để đại diện cho countryCode, bởi vì có thể không có value tương ứng với key được truyền vào. Nếu chúng ta cố gắng tìm kiếm "The Emerald City", thì giá trị value của countryCode sẽ là nil vì nó không tồn tại trong dictionary. Thêm đoạn code sau vào playground:

let optionalName = Optional<String>.some("Princess Moana")
if let name = optionalName {}

Check type của name, bạn sẽ thấy nó là String. Cấu trúc if-let ở trên được gọi là Optional Binding là 1 generic transformation of sorts - sự chuyển đổi động giữa các loại. Nó sẽ lấy giá trị generic của kiểu T? và trả về cho bạn giá trị generic của kiểu T. Nói 1 cách đơn giản, nếu optionalName mà không bằng nil, code sẽ chạy vào bên trong {} và ta sẽ sử dụng giá trị của optionalName thông qua name. Bạn có thể sử dụng if let với bất kỳ kiểu cụ thể nào.