Generics are basically placeholders for types. They help you write specific code that can be used with (almost) any type.
Suppose you’re really tired of the whole if let
dance. You want to write a function that will do that for you, so you can save some lines of code.
Generics to the rescue! You could just do this:
func ifExec(_ t: T?, _ f: (T) -> T) -> T? { if let unwrappedT = t { return f(unwrappedT) } return nil }
This function:
- accepts an optional object called
t
of typeT
, - a function
f
that accepts a non-optional object of the same type and returns an object of the same type
Inside the function, we:
- unwrap the optional and
- return the results of whatever
f
does - (or
nil
, ift
isnil
).
So, for instance, suppose you have a function that will capitalize the first letter of a String
. It might look like this:
func capitalizeFirstLetter(string: String) -> String { string.prefix(1).uppercased() + string.dropFirst() }
The function we wrote above that uses generics would allow you to condense this:
var name: String? = "elliot" if let unwrappedName = name { name = capitalizeFirstLetter(unwrappedName) }
into this:
var name: String? = "elliot" name = ifExec(name, capitalizeFirstLetter)
Why generics?
Is there another way to write ifExec
without generics? Well, not really. You could replace T
with AnyObject
or Any
, but then:
- you’d have to cast anything you passed into an
AnyObject
, which is gross - you’d have to then force cast the returned object to what you need it to be
- any function
f
you passed in toifExec
would have to accept an AnyObject and return an AnyObject – which not many functions do (and certainly ourcapitalizeFirstLetter
doesn’t)
The key here is that generics allow you not to care about what the actual type is, and focus on the interactions between the type(s) and function(s).