func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
swapTwoInts(::) 函数很实用,但是它只能用于 Int 值。如果你想交换两个 String 值,或者两个 Double 值,你只能再写更多的函数。
一、函数
为了解决类似上面的问题,我们可以用一个泛型函数去表示一个函数
func swapTwo<T>(_ a: inout T, _ b: inout T) {
let temporaryA = a
a = b
b = temporaryA
}
var someInt = 3
var anotherInt = 107
swapTwo(&someInt, &anotherInt)
var someString = “3”
var anotherString = “17”
swap(&someString, &anotherString)
- 占位符类型 T 就是一个类型形式参数指定并且命名一个占位符类型,紧挨着写在函数名后面的一对尖括号里(比如 <T> )。
- 大多数情况下,类型形式参数的名字要有描述性,比如 Dictionary<Key, Value> 中的 Key 和 Value ,借此告知读者类型形式参数和泛型类型、泛型用到的函数之间的关系。但是,他们之间的关系没有意义时,一般按惯例用单个字母命名,比如 T 、 U 、 V ,比如上面的 swapTwoValues(::) 函数中的 T 。
1.2、泛型函数赋给变量
func test<T1,T2>(t1:T1,t2:T2){
print(t1,t2)
}
var fn:(Int,Int)->() = test
var fn1:(Int,String)->() = test
var fn2:(String,String)->() = test
fn(10,20)
fn1(10,"你好")
fn2("大家好","你好")
二、类型
除了泛型函数,Swift允许你定义自己的泛型类型。它们是可以用于任意类型的自定义类、结构体、枚举,和 Array 、 Dictionary 方式类似。
class Stack<Element>{
var items = [Element]()
func push(_ item: Element) {
items.append(item)
}
func pop() -> Element {
return items.removeLast()
}
}
class Stack<Element>{
var items = [Element]()
func push(_ item: Element) {
items.append(item)
}
func pop() -> Element {
return items.removeLast()
}
}
var stack = Stack<String>()
stack.push(“ssss”)
var stack1 = Stack<Int>()
stack1.push(10)
- 这个泛型的 Stack 和非泛型版本的本质上是一样的,只是用一个叫做 Element 的类型形式参数代替了实际的 Int 类型。这个类型形式参数写在一对尖括号( <Element> )里,紧跟在结构体名字后面。Element 为稍后提供的”某类型 Element “定义了一个占位符名称。这个未来的类型可以在结构体定义内部任何位置以” Element “引用。在这个例子中,有三个地方将 Element 作为一个占位符使用:
struct Stack<Element> {
var items = [Element]()
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
}
stackOfStrings = Stack<String>()
stackOfStrings.push(“uno”)
stackOfStrings.push(“dos”)
stackOfStrings.push(“tres”)
stackOfStrings.push(“cuatro”)
enum Socre<T> {
case point(T)
case grade(String)
}
let score0 = Socre<Int>.grade("")
let score1 = Socre.point(3)
let score2 = Socre<Int>.point(100)
三、关联类型
在泛型函数中,你可以在函数名后面紧跟着一个类型形式参数,来表明泛型。然在协议中你不能这样去表示,编译器是不允许的。你需要在内部声明一个关联类型来表示泛型。
protocol Test {
associatedtype Element
mutating func append(_ item: Element)
var count: Int { get }
subscript(i: Int) -> Element { get }
}
- 用associatedtype关键字可以声明一个关联类型
- 关联类型作用:在协议中声明一个泛型类
class Student: Test {
typealias Element = String// 给关联类型设定真实类型
func append(_ item: String) {}
var count: Int = 0
subscript(i: Int) -> String {
return “333”
}
}
class Person: Test {
typealias Element = Int// 给关联类型设定真实类型
func append(_ item: Int) {}
var count: Int = 0
subscript(i: Int) -> Int {
return 333
}
}
四、类型约束
protocol Runnable{}
class Person{}
func swapValues<T:Person&Runnable>(a:T,b:T){}
protocol Stackable {
associatedtype Eleement:Equatable
}
class Stack<E:Equatable>:Stackable{
typealias Eleement = E
}
var stack = Stack<Int>()
五、不透明类型
如果协议中有 associatedtype,
截屏2020-01-14下午5.12.29.png
解决方案1:使用泛型
func test<T:Runnable>(a:Int) -> T{
return Person() as! T
}
解决方案2:不透明类型
使用some关键字声明一个不透明类型
func test(a:Int) -> some Runnable{
return Person()
}
网友评论