Generic Types
Part of the Object Oriented Programming section of Coddy's Swift journey. Lesson 35 of 57.
Types can be generic too. Stack<Element> works for any element type, the same way Swift's own Array<Element> does. The placeholder is used for properties and method signatures:
struct Stack<Element> {
private var items: [Element] = []
mutating func push(_ item: Element) { items.append(item) }
mutating func pop() -> Element? { items.popLast() }
var top: Element? { items.last }
var isEmpty: Bool { items.isEmpty }
}
var numbers = Stack<Int>()
numbers.push(3)
numbers.push(9)
print(numbers.pop()!)
var words = Stack<String>()
words.push("hi")
print(words.top!)Output:
9
hiEach concrete version is its own type: a Stack<Int> only accepts integers, and the compiler rejects anything else. Often the type can be inferred from the initial value:
var s = Stack<Int>()
s.push(4)
// s.push("four") // error: cannot convert String to Int
let names = ["a", "b"] // inferred as Array<String>A generic type can have several placeholders and constraints:
struct Pair<First, Second> {
let first: First
let second: Second
func swapped() -> Pair<Second, First> { Pair<Second, First>(first: second, second: first) }
}
struct Leaderboard<Entry: Comparable> {
var entries: [Entry] = []
mutating func add(_ e: Entry) { entries.append(e); entries.sort(by: >) }
}
let p = Pair(first: "age", second: 36).swapped()
print(p.first)
var board = Leaderboard<Int>()
board.add(5)
board.add(12)
print(board.entries)Output:
36
[12, 5]A constrained extension adds members only when the placeholder meets a condition, such as a sum for stacks of integers:
struct Stack<Element> {
var items: [Element] = []
mutating func push(_ item: Element) { items.append(item) }
}
extension Stack where Element == Int {
var sum: Int { items.reduce(0, +) }
}
var s = Stack<Int>()
s.push(4)
s.push(6)
print(s.sum)Output:
10Challenge
EasyWrite the generic struct History<Item> that remembers the last limit items. add(_:) appends an item and drops the oldest when there are more than limit; latest returns the newest item (optional); all returns the items from oldest to newest. Add a constrained extension for Item == Int with an average property (integer division, 0 when empty).
The supplied code reads the limit, a line of words and a line of numbers. It fills a History<String> and a History<Int> and prints the kept words, the latest word, the kept numbers and their average.
Your code goes in History.swift. main.swift holds the supplied input/output code and cannot be edited.
Try it yourself
// Supplied input/output code: keep it as it is
var input: [String] = []
while let line = readLine() { input.append(line) }
let limit = Int(input[0])!
var words = History<String>(limit: limit)
for w in input[1].split(separator: ",") { words.add(String(w)) }
var numbers = History<Int>(limit: limit)
for n in input[2].split(separator: ",") { numbers.add(Int(n)!) }
print(words.all.joined(separator: " "))
print(words.latest ?? "none")
print(numbers.all.map { String($0) }.joined(separator: " "))
print(numbers.average)
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Classes and Objects
Working With FilesDefining ClassesMethods and selfInitializersClasses Are ReferencesRecap - Library Card4Encapsulation
Access ControlRead-Only From OutsideGuarding StateFailable InitializersRecap - Bank Account7Polymorphism and Generics
PolymorphismType CastingGeneric FunctionsGeneric TypesRecap - Shape Calculator5Inheritance
SubclassesOverriding MethodsInitializers and superfinal and HierarchiesRecap - Employee Hierarchy11Project: Library Management
Books and MembersBorrowing BooksPractice on your own: Swift playground