It is possible to extend an existing class or struct by adding functions to the type. The new method can be used anywhere the extension is in scope.
extension Clock {
func tick() -> String {
return "tock"
}
}
Clock().tick()
$R1: String = "tock"
Adding extensions to types doesn't require the code for the original to be changed; so it's possible to extend the built-in types with additional functionality:
25> 1.negate()
error: value of type 'Int' has no member 'negate'
26> extension Int {
27. func negate() -> Int {
28. return -self
29. }
30. }
31> 1.negate()
$R2: Int = -1
This exposes the fact that a literal 1
is actually a struct type; we can add new functions to the structure that perform different operations. Since it's an immutable struct, we can't change the value of the constant; but we can add constant properties and functions to the mix.
This hasn't added negation to all integer types though, only the Int
type -- which is a typealias for the platform's default word size (64 bits on a 64-bit system, 32 bits on a 32-bit system).
Int8(1).negate()
error: value of type 'Int8' has no member 'negate'
This can be solved by adding an extension to a protocol instead of the actual type. A protocol is like an interface in other languages; it's an abstract type representation that has no implementation body, and types (both struct and class) can adopt protocols at definition time.
The Int
type adopts several protocols; IntegerType
(which is the parent of all integer types, including unsigned ones) and SignedNumberType
(amongst others). We can add the extension to this protocol instead:
extension SignedNumberType {
func negate() -> Self {
return -self
}
}
Note that the return type of the negate
function is now Self
--this is a special type for generic values that returns the actual type of the object, rather than a generic implementation of the SignedNumberType
interface. This allows an Int8
to be negated into an Int8
, and an Int16
to be negated to an Int16
.
Int8(1).negate()
$R3: Int8 = -1
Int16(1).negate()
$R4: Int16 = -1
Extensions can be used to retroactively adopt procotols for classes. Unlike Go, which uses structural interface adoption to automatically align interfaces, Swift uses an opt-in model where types explicitly have to say that they adopt the protocol in order to use it. Unlike Java or C#, these don't have to be done at the type definition time; they can be added retroactively through extensions.
extension Clock: CustomStringConvertible {
public var description: String {
return "The time is: \(time.display)"
}
}
print(Clock())
The time is: 13:15:23
{{ parent.title || parent.header.title}}
{{ parent.tldr }}
{{ parent.linkDescription }}
{{ parent.urlSource.name }}