浅谈 Swift 中的泛型

Objective-C缺乏一个重要特性:不支持泛型。幸运地是,Swift拥有这一特性。泛型允许你声明的函数、类以及结构体支持不同的数据类型。

提出问题

优秀的泛型使用案例中,最常见的例子当属对栈(Stack)的操作。栈作为容器有两种操作:一.压入(Push)操作添加项到容器中;二.弹出(Pop)操作将最近添加项从容器移除。首先我们用非泛型方式设计。最后代码如下所示:

1234567891011121314
class IntStack{  // 采用数组作为容器保存数据 类型为Int  private var stackItems:[Int] = []  // 入栈操作 即Push 添加最新数据到容器最顶部  func pushItem(item:Int){    stackItems.append(item)      }  // 出栈操作 即Pop 将容器最顶部数据移除  func popItem()->Int?{    let lastItem = stackItems.last    stackItems.removeLast()    return lastItem  }}

该栈能够处理Int类型数据。这看起来不错,但是倘若要建立一个能够处理String类型的,我们又该如何实现呢?我们需要替换所有IntString,不过这显然是一个糟糕的解决方法。此外另外一种方法乍看之下灰常不错,如下:

1234567891011121314
class AnyObjectStack{  // 采用数组作为容器保存数据 类型为AnyObject  private var stackItems:[AnyObject] = []  // 入栈操作 即Push 添加最新数据到容器最顶部  func pushItem(item:AnyObject){    stackItems.append(item)      }  // 出栈操作 即Pop 将容器最顶部数据移除  func popItem()->AnyObject?{    let lastItem = stackItems.last    stackItems.removeLast()    return lastItem  }    }

此处,我们合理地使用AnyObject类型,那么现在能够将String类型数据压入到栈中了,对么?不过这种情况下我们就失去了数据类型的安全,并且每当我们对栈进行操作时,都需要进行一系列繁琐的类型转换(casting操作,使用as来进行类型转换)。

解决方案

参照泛型的特性,我们能够定义一个泛型类型,这看起来像一个占位符。使用泛型后的示例代码如下:

123456789101112131415
class Stack<T> {

private var stackItems: [T] = []  

func pushItem(item:T) {    stackItems.append(item)  }  

func popItem() -> T? {    let lastItem = stackItems.last    stackItems.removeLast()    return lastItem  }

}

泛型定义方式:由一对尖括号(<>)包裹,命名方式通常为大写字母开头(这里我们命名为T)。在初始化阶段,我们通过明确的类型(这里为Int)来定义参数,之后编译器将所有的泛型T替换成Int类型:

12345678
// 指定了泛型T 就是 Int // 编译器会替换所有T为Intlet aStack = Stack<Int>()

aStack.pushItem(10)if let lastItem = aStack.popItem() {  print("last item: \(lastItem)")}

如此实现的栈,最大优势在于能够匹配任何类型。

类型约束

这里存在一个缺点:尽管泛型能够代表任何类型,我们对它的操作也是比较有局限性的。仅仅是比较两个泛型都是不支持的,请看如下代码:

123456789101112131415161718192021222324
class Stack<T> {

private var stackItems: [T] = []

func pushItem(item:T) {    stackItems.append(item)  }

func popItem() -> T? {    let lastItem = stackItems.last    stackItems.removeLast()    return lastItem  }

func isItemInStack(item:T) -> Bool {    var found = false    for stackItem in stackItems {      if stackItem == item { //编译报错!!!!!!!!!!        found = true      }    }    return found  }}

注意到函数isItemInSatck(item:T)中,我们得到了一个编译错误,因为两个参数没有实现Equtable协议的话,类型值是不能进行比较的。实际上我们可以为泛型增加约束条件来解决这个问题。在本例中,通过对第一行进行修改,我们让泛型T遵循Equatable协议:

123456789101112131415161718192021222324
class Stack<T:Equatable> {

private var stackItems: [T] = []

func pushItem(item:T) {    .append(item)  }

func popItem() -> T? {    let lastItem = stackItems.last    stackItems.removeLast()    return lastItem  }

func isItemInStack(item:T) -> Bool {    var found = false    for stackItem in stackItems {      if stackItem == item {        ound = true      }    }    return found  }}

总结

就像众多其他编程语言一样,你也能够在Swift中利用泛型这一特性。倘若你想要写一个库,泛型是非常好用的特性。

来源: <http://swift.gg/2015/09/16/swift-generics/>

来自为知笔记(Wiz)

时间: 2024-10-25 02:47:01

浅谈 Swift 中的泛型的相关文章

浅谈swift中的内存管理

Swift使用自动引用计数(ARC(Automatic Reference Count))来管理应用程序的内存使用.这表示内存管理已经是Swift的一部分,在大多数情况下,你并不需要考虑内存的管理.当实例并不再被需要时,ARC会自动释放这些实例所使用的内存. 内存管理:针对的是实例的内存占用的管理(放在堆里面) 实例:1:由class类型构建的实例,2:闭包对象 下面我们来写一个实例来证明一下 class Person { var name: String init(name: String )

浅谈swift中的那些类,结构以及初始化的操作

首先呢,我们先声明一个类 class Parent { //声明一个属性 var p1: String = "abc" //声明一个方法 func m() { print("parent m") } //声明一个静态的方法 final func n(){ } } 然后我们new一个Parent类(注意了,在swift中是没有new的,如果想new 一个的话, 直接调用该类就可以了) var par = Parent() 调用parent的方法和属性 par.m()

浅谈swift中的函数类型和闭包

在讲swift的函数类型之前,我们先回忆一下我们以前学的定义一个swift的函数 func add(a: Int,b: Int) -> Int { return a + b } 好了, 我们开始我们函数类型的讲解 上面这个函数的类型是(Int ,Int)->Int 使用函数类型 我们都知道, 在swift中 , 函数类型就像其他数据类型一样,也就意味着我们可以给一个函数的常量或者是变量赋值 var f2: (Int,Int)-> Int = add f2(2,3) //结果为5 好了,接

浅谈 Swift 中的 Optionals

input[type="date"].form-control,.input-group-sm>input[type="date"].input-group-addon,.input-group-sm>.input-group-btn>input[type="date"].btn,input[type="time"].input-sm,.form-horizontal .form-group-sm input

浅谈Java中的泛型

泛型是Java自JDK5开始支持的新特性,主要用来保证类型安全.另外泛型也让代码含义更加明确清晰,增加了代码的可读性. 泛型的声明和使用 在类声明时在类名后面声明泛型,比如MyList<T>,其中T就是泛型,相当于一个类型变量,表示MyList类期望操作的类型. public class MyList<T> { public void add(T t) { } } 当定义MyList变量.创建MyList对象.声明MyList子类时就可以为泛型T指定具体类型 public stat

浅谈swift中的那些结构体和枚举

首先呢, 我们先写一个struct struct Point { var x = 0 var y = 1 mutating func change (newX:Int,newY: Int) { x = newX y = newY } } var p1 = Point(x: 4, y: 4) p1.x //4   在这里呢,可能有人会问,为什么在func前面要加一个mutating, 如果不加的话, 编译器抛出错误,报出不能在实例方法中修改属性值 所以呢, 我们必须如果想改变成员变量的话, 就必须

浅谈C#中new、override、virtual关键字的区别

OO思想现在已经在软件开发项目中广泛应用,其中最重要的一个特性就是继承,最近偶简单的学习了下在设计模式中涉及到继承这个特性时,所需要用到的关键字,其中有一些关键点,特地整理出来. 一.New 在C#中,new这个关键字使用频率非常高,主要有3个功能: a)   作为运算符用来创建一个对象和调用构造函数. b)   作为修饰符. c)   用于在泛型声明中约束可能用作类型参数的参数的类型. 在本文中,只具体介绍new作为修饰符的作用,在用作修饰符时,new关键字可以在派生类中隐藏基类的方法,也就说

浅谈数据库系统中的cache(转)

http://www.cnblogs.com/benshan/archive/2013/05/26/3099719.html 浅谈数据库系统中的cache(转) Cache和Buffer是两个不同的概念,简单的说,Cache是加速"读",而buffer是缓冲"写",前者解决读的问题,保存从磁盘上读出 的数据,后者是解决写的问题,保存即将要写入到磁盘上的数据.在很多情况下,这两个名词并没有严格区分,常常把读写混合类型称为buffer cache,本文后续的论述中,统一

【转】浅谈Java中的equals和==

浅谈Java中的equals和== 在初学Java时,可能会经常碰到下面的代码: 1 String str1 = new String("hello"); 2 String str2 = new String("hello"); 3 4 System.out.println(str1==str2); 5 System.out.println(str1.equals(str2)); 为什么第4行和第5行的输出结果不一样?==和equals方法之间的区别是什么?如果在初