Generics (generieke types) maken het mogelijk om flexibele en herbruikbare functies en types te definiëren. Een belangrijk kenmerk van Swift is het behoud van type safety: de compiler controleert het gebruik van specifieke types tijdens de compileertijd. Generieke types kunnen worden beperkt via where-voorwaarden, erfelijkheid van protocollen en hun combinaties.
Code voorbeeld:
func swapValues<T>(_ a: inout T, _ b: inout T) { let temp = a a = b b = temp } protocol Drawable { func draw() } func drawAll<T: Drawable>(_ items: [T]) { for item in items { item.draw() } } // Beperking via een protocol en where-voorwaarde func compareValues<T: Equatable>(_ a: T, _ b: T) -> Bool { return a == b }
Kunnen generieke functies fungeren als overloaded functies? Hoe kiest de compiler de juiste implementatie?
Antwoord: Ja, generieke functies kunnen worden overladen met gewone en andere generieke functies. De compiler probeert de meest specifieke implementatie te kiezen. Voorbeeld:
func printValue(_ value: Int) { print("Int: \(value)") } func printValue<T>(_ value: T) { print("Generic: \(value)") } printValue(5) // Int: 5 printValue("Swift") // Generic: Swift
Verhaal
Het team schreef een generieke uitbreiding voor een array om de index te zoeken, maar vergat het type te beperken via Equatable. Dit leidde tot een compileerfout bij het proberen toe te passen van de extensie op een array met niet-Equatable elementen.
Verhaal
In het project probeerden ze een cache van generieke objecten te implementeren zonder een typebeperking. Als gevolg hiervan traden er runtime crashes op bij het proberen downcasten – echte veilige gebruik kon worden bereikt via protocollen met associated type en constraints.
Verhaal
Ontwikkelaars implementeerden een generieke klasse, maar vergaten bij het overerven en overschrijven de noodzaak om de volledige generieke parameter van de afgeleide aan te geven. Hierdoor compileerde de code niet en was er een volledige herstructurering van de typehiërarchie nodig.