Last time we looked at error handling in Swift
. In this article we will look at Generics and how they can make use of one set of types to define a new set of types. Let’s start with a simple Car struct. Car
is a concrete type.
struct Car {
var make: String
}
On the other hand, we create the struct Owner
as a generic type and T
is its type parameter:
struct Owner<T> {}
Finally, driver
is a specialization of the generic type.
let driver = Owner<Car>()
Having generics allows us to declare array in various ways:
let array1 = []
let array2 = [Int]()
let array3 = Array<Int>()
While array1
is a NSArray
, both array2
and array3
are representing an Array
of type Int
, so their syntax is equivalent.
Let’s look at how the type parameter
can be exploited further:
struct Owner<T> {
var name: String
var firstOwner: T
var reuseOwner: T
}
We can have the same type parameter used also as a struct member. Then creating an Owner
becomes more interesting:
let me = Owner(name: "John", firstOwner: Car(make: "Audi"), reuseOwner: Car(make: "BMW"))
Obviously, generics have type inferral
:
let array4 = Array(arrayLiteral: 1, 2, 3)
In this case array4
becomes an Array<Int>
by type inference.
For dictionaries there is more information offered. A dictionary is a struct defined as Dictionary<Key : Hashable, Value>
. What follows after : is either a supertype
or a protocol
the key conforms to, and it is named type constraint. Dictionaries also have type inferral:
let ints: [Int: String] = [10: "ten"]
let ages = ["John": 39]
In this case, the ages
dictionary is inferred to be of <String, Int>
type.
As you might have expected, generics
work well on optionals
too:
enum Optional<T> {
case None
case Some(T)
}
So in this case we have the Optional type that can either be of Some type, or nil. Then we can test which of the two cases we need to deal with, and react accordingly:
let employed = Optional<String>.Some("programmer")
let unemployed = Optional<String>.None
if let job = employed {
print(employed)
} else {
print(unemployed)
}
Finally, generics can be used with functions as well. You can notice the type parameters
are the same as the function arguments
:
func concatenate<A, B>(a: A, _ b: B) -> String {
return "\(a)\(b)"
}
concatenate("$", 5)
The above function call will print out as you expected:
$5
The source code is posted on Github
, as usual.
Until next time!