• Swift语言中的泛型编程 【Generic】【Part 1】


    泛型 

    泛型编程让你可以编写更具扩展性、易重用的功能,代码使用时的类型取决于你所定义的业务需求。你可以编写避免重复的代码,目标更加清晰明确,风格抽象。

    泛型是Swift语言的一项强大的特性,基本上所有的swift标准库都建立在泛型代码上。实际上,你在这本书的时候不知不觉地接触到泛型,它贯穿始终。例如Swift的数组和字典类型都是泛型集合。你可以创建用于存放整数值的数组,也创建数组存储字符串,其他所有能在Swift中创建的类型都可以创建到数组中。类似的,你可以创建字典来存储数值或其他指定的类型,毫无拘束。

     

    泛型编程所解决的问题

    这里是一个标准的,没有使用泛型的swapTwoInts函数,使用两个整数输入参数:

          func swapTwoInts(inout a: Int, inout b: Int) {

             let temporaryA = a

             a = b

             b = temporaryA

         }

    这个函数使用可读写的输入参数作为交换变量(变量a,变量b),该语法具体见“In-Out Parameters”章节

    swapTwoInts函数交换两个输入参数的初始值(将a和b交换),先将b置入a,再将a置入b。你可以调用这个函数去交换两个整数变量。

         var someInt = 3

         var anotherInt = 107

         swapTwoInts(&someInt, &anotherInt)

         println("someInt is now (someInt), and anotherInt is now (anotherInt)")

         // prints "someInt is now 107, and anotherInt is now 3"

    swapTwoInts函数是有用的,但它只能用于整数(Int)的类型,如果你想用它来交换两个字符串,或两个长整数类型,你就需要写更多的函数,命名出诸如swapTwoStrings或swapTwoDoubles这样的函数名字来,例如下面这两个函数:

         func swapTwoStrings(inout a: String, inout b: String) {

             let temporaryA = a

             a = b

             b = temporaryA

         }    

         func swapTwoDoubles(inout a: Double, inout b: Double) {

             let temporaryA = a

             a = b

             b = temporaryA

         }

    你可能注意到函数过程主体上,swapTwoInts, swapTwoStrings和 swapTwoDoubles是一样的,只是他们所接入的输入值有所区别(Int,String和Double)。

    所以我们把想把它改得更加有用,更具延展性,有没有办法编写一个可以交换任何类型的单一函数? Okay, 泛型就是用来解决这一类的问题。(下面就来设计这几个函数的泛型版本)

     

    批注:

    对于上面的三个函数,最主要的一点是a、b两个值的定义类型是相同的。如果a和b两个值不是相同类型,就无法在函数内对其进行交换。Swift是一个类型安全的语言,举例来说,它不允许字符串类型和长整数类型的两个变量直接进行交换,尝试这样做将会得到一个编译时的错误信息。

      

    泛型函数

    泛型函数可以适应各种类型,这个是泛型版本的swapTwoInts函数,命名为swapTwoValues:

     

         func swapTwoValues<T>(inout a: T, inout b: T) {

             let temporaryA = a

             a = b

             b = temporaryA

         }

    swapTwoValues函数主体和swapTwoInts函数主体是一样的,但是程序的swapTwoValues和swapTwoInts的第一行程序是有点点区别的,我们对比一下:

         func swapTwoInts(inout a: Int, inout b: Int)

         func swapTwoValues<T>(inout a: T, inout b: T)

     

    泛型版本的函数使用了一个占位符类型(在这里命名为T)规范为一个实际的命名值(比如Int String 或Double).占位符并不说明T一定是某个类型,但它说明a和b一定是相同的T类型,不论T代表什么,实际产生的类型取决于调用的时候参数所传值的类型。

    其他的区别在于泛型函数名称swapTwoValues后面跟随一个<T>。括号告诉Swift占位符T代表用于swapTwoValues的泛类。因为T已被说明是一个占位符,Swift并不立即查找名为“T”类型,等调用的时候才判断。

    现在,swapTwoValues函数可以像swapTwoInts一样调用了,表面上它可以传入任何两个数值,只要数值的类型是一样就可以了。每次swapTwoValues调用的时候,类型T就替换为输入参数值所使用的类型。

    正如下面的两个例子程序一样,T分别被置换为Int和string类型。

         var someInt = 3

         var anotherInt = 107

         swapTwoValues(&someInt, &anotherInt)

         // someInt is now 107, and anotherInt is now 3

          

         var someString = "hello"

         var anotherString = "world"

         swapTwoValues(&someString, &anotherString)

         // someString is now "world", and anotherString is now "hello" 

    批注:上面的定义swapTwoValue函数设计想法来自一个泛型函数,叫swap,它已经是Swift的标准库函数,可供我们在app开发中调用,如果真需要一个叫swapTwoValues的函数的话,最好还是使用现有的swap函数,我们无须自行设计一个。

     

    类型参数

    在上文swapTwoValues示例函数中,占位符T是一个类型参数的例子。类型参数指定、命名占位符类型,定义上跟随函数的名字并使用括号包含“如<T>”

    一个类型参数可以用于解释函数的参数属于什么类型(比如swapTwoValues函数的a和b参数的类型);可以解释函数返回的泛型,也可以解释函数执行体中所使用的泛型,在这些应用场景中,占位符所代表的类型将在调用到的时候被实际类型代替。(在swapTwoValues示例程序中,类型T在函数第一次调用的时候就被Int这个具体类型所代替,然后在第二次调用的时候却被string类型所代替)

    你可以在括号(<T>)中写入更多的类型参数,之间使用逗号隔开(如<T1,T2,T3>)。

     

    命名类型参数

    在上文简单的例子下你可以写单个占位符实现泛型函数或泛型类型(比如上文swapTwoValues泛型函数,或存储单种类型的泛型集合,比如Array),通常我们使用T来命名类型参数,但你可以使用任何符合规则的标识符来命名类型参数。

    如果你在定义复杂的泛型函数,或具体多种参数的泛型类型,你可以使用更长更详细的名字命名类型参数。比如Swift的Dictionary类型有两个类型参数,keys和values,分别代表键和值。如果你自已写一个Dictionary类,可以把这两个类型参数命名为KeyType和ValueType ,这样就更方便你记住它们在代码中是用来做什么用途了。

    批注:

    经通常使用首字母为大写的骆驼式命名法(UpperCamelCase),比如(T或KeyType),这样我们可以直观判断这个符号是类型占位符,而不是数值。

     

    泛型类型

    除了定义泛型函数,Swift允许你定义自已的泛型类型,可以是自定义的类,结构体,或枚举。就象Array和Dictionary类。

    本章将向你展示如写编写一个泛型堆栈Stack类。堆栈是一堆有序的数值,类似数组,但相对Array数组它有更严格的操作要求。 数组允许你存储和删除数组中任一位置的元素。而堆栈只允许你添加新的内容到它的栈顶(就是push新值到栈中),同样的,堆栈允许你删除元素,但也只能是从栈顶删除(这个叫poping操作)

     

    批注:

    堆栈的概念在UINavigationController类用于视图控制器处理。你可以调用UINavigationController的pushViewController:animated: 方法来把一个视图控制器压入视图导航堆栈(navigation stack),使用popViewControllerAnimated: 方法取回(弹出)之前的视图控制器。堆栈可以让你实现“最进,先出”的集合管理需求。

     

    下图描述了堆栈如何实现压入和弹出(push/pop)的行为:

      

    1、         现在堆栈里面有三个数值

    2、         第四个数值使用压入操作“Push”进入堆栈的顶部

    3、         现在堆栈中有四个值,最后压入的在最顶上。

    4、         最顶上的值可以移出或叫做“弹出”(poped)

    5、         在弹出一个数值之后,堆栈又是只有三个数值

    这样就是非泛型的堆栈的工作情况,我们举例堆栈使用的数值是整数类型

         struct IntStack {

             var items = Int[]()

             mutating func push(item: Int) {

                 items.append(item)

             }

             mutating func pop() -> Int {

                 return items.removeLast()

             }

         }

    这个结构有一个属性是数组Array,还有一个存储数值的item用于表示堆栈存储的内容,堆栈提供两个方法,push和pop,表示压栈和弹出,这些方法标置了mutating,因为它们需要修改结构里面的item元素数组。

    在IntStack整型堆栈中,工作类型是Int整理,但是,如果把它定义成一个泛型的堆栈类将会更有意义,我们能够通过它处理多种类型的数据。

    下面是泛型版本的堆栈代码:

         struct Stack<T> {

             var items = T[]()

             mutating func push(item: T) {

                 items.append(item)

             }

             mutating func pop() -> T {

                 return items.removeLast()

             }

         }

    重申一下泛型版本的Stack结构体和非泛型版本的基本一样,只是把Int类型置换成T占位符,这个东西每每加上一个菱形括号,写在结构体名称后面,定义、实例化的时候都用上。

    T代表占位符表示将有一个具体的类型在后面会提供置换,这个未知类型T用在了结构体的诸多地方,我们总结一下有三个地方:

    1、  创建属性items,存储堆栈数据,用T类型进行了初始化,虽然是空的。

    2、  声明push方法有一个参数item,它是T类型,这样才能压入堆栈。

    3、  声明pop函数返回值与压入栈是一样的T类型。

    实例化Stack的方法和Array数组、Dictionary字典的实例化是一样的,把Stack中的元素需要泛化成什么样的类型写在菱括号中,跟在Stack类名后面。下面是示例代码。

         var stackOfStrings = Stack<String>()

         stackOfStrings.push("uno")

         stackOfStrings.push("dos")

         stackOfStrings.push("tres")

         stackOfStrings.push("cuatro")

         // the stack now contains 4 strings

    下面是stackOfStrings字符串在堆栈在内存中的实际样子,我们将压入四个值。

     

    从堆栈中pop弹出数值将取回最后压入的元素,栈顶的“cuatro”

         let fromTheTop = stackOfStrings.pop()

         // fromTheTop is equal to "cuatro", and the stack now contains 3 strings

     

    下面是弹出一个元素后的堆栈情况:

      

    因为是泛化类型,堆栈能够使用Swift中各种类型来实例化,使用起来的风格和Array数组、Dictionary字典一样。

     

    未完待续,今天学习和翻译自《The Swift Programming Language》电子书)【Generic】章节

  • 相关阅读:
    保留最大的数
    彩色宝石项链
    [leetcode] 403. Frog Jump
    [leetcode] 402. Remove K Digits
    Linux 更改时区、时间
    Linux系统时间同步方法
    mysql 5.7.28 地理位置计算详解
    springboot微服务项目集成为单体
    地理空间数据Geometry在MySQL中使用(一)
    mysql中geometry类型的简单使用
  • 原文地址:https://www.cnblogs.com/bailey/p/3774839.html
Copyright © 2020-2023  润新知