- Generics can parameterize types, improve code reuse and reduce code volume.
func swapValues<T>(_ a: inout T, _ b: inout T) { (a, b) = (b, a) } var n1 = 10 var n2 = 20 swapValue(&n1, &n2)
Generic functions are assigned to variables
var fn:(inout: Int, inout: Int) -> () = swapValues fn(&n1, &n2)
var test<T1, T2>(_ t1: T1, _ t2: T2) {} var fn: (Int, Double) -> () = test
Generic generic
class Stack<E> { var elements = [E]() func push(_ element: E) { elements.append(element) } func pop() -> E { elements.removeLast() } func top() -> E { elements.last! } func size() -> Int { elements.count } } var stack = Stack<Int>() class SubStack<E> : Stack<E> {}
Structural generics
struct Stack<E> { var elements = [E]() mutating func push(_ element: E) { elements.append(element) } mutating func pop() -> E { elements.removeLast() } func top() -> E { elements.last! } func size() -> Int { elements.count } }
Enumeration generic
enum Score<T> { case point<T> case grade(String) } let score0 = Score<Int>.point(100) let score1 = Score.point(99) let score2 = Score.point(99.5) let score3 = Score<Int>.grade("A") //Generic types must be initialized
Association Type
- Role of associative types: Define a placeholder name for the type used in the protocol
- There can be multiple association types in the protocol
protocol Stackable { associatedtype Element //Association type //Associated type Element2 // Associated type mutating func push(_ element: Element) mutating func pop() -> Element func top() -> Element func size() -> Int }
class StringStack : Stackable { //Set the true type for the associated type //typealias Element = String var elements = [String]() func push(_ element: String) { elements.append(element) } func pop() -> String { elements.removeLast() } func top() -> String { elements.last! } func size() -> Int { elements.count } }
Type constraints:
protocol Stackable { associatedtype : Equatable } class Stack<E: Equatable> : Stackable { typealias Element = E }
func equal<S1: Stackable, S2: Stackable>(_ s1: S1, _ s2: S2) -> BOOl where S1.Element == S2.Element, S1.Element : Hashable { return false }
Notes on protocol types:
- Association types cannot be used as return values directly
protocol Runnable { associatedtype Speed var speed: Speed { get } } class Person : Runnable { var speed: Double { 0.0 } } class Car : Runnable { var speed: Int { 0 } func run() {} }
- Solution 1
func get<T: Runnable>(_ type: Int) -> T { if type == 0 { return Student() as! T } return Person() as! T } var r1 = get(0) var r1 = get(1)
- Solution 2
- Opacity type, can only return one type, shield specific type, only expose protocol type
func get(_ type: Int) -> some Runable { //if type == 0 { // return Student() //} return Person() } var r1 = get(0) r1.speed //Runable.Speed //ri.run() cannot be called
- some can be used not only on return value types, but also on attribute types.
protocol Runnable { associatedtype Speed } class Dog : Runnable { typealias Speed = Double } class Person { var pet: some Runnable { return Dog() } } //Or the following is a way of not seeing the real type protocol Runnable { } class Dog : Runnable { } class Person { var pet: Runnable { return Dog() } }