//泛型函数,泛型枚举,泛型结构,泛型类,泛型约束,关联泛型


/**
*  泛型概览    

在强类型语言中,你需要去定义诸如addInts, addFloats, addDoubles 等方法来正确地处理参数及返回值。
许多编程语言已经解决了这个问题。例如,在C++中,使用Template来解决。而Swift,Java和C#则采用了泛型来解决这个问题。泛型


Swift中的数组和字典类型就是使用泛型的经典例子。
*/
/**
*  泛型(generic)可以使我们在程序代码中定义一些可变的部分,在运行的时候指定。使用泛型可以最大限度地重用代码、保护类型的安全以及提高性能。在Swift集合类中,已经采用了泛型。
一、一个问题的思考
怎样定义一个函数来判断两个参数是否相等呢?

相比Object-C,Swift中的数组和字典都是类型安全的。一个Int型数组只可以保存Int而不可以保存String。这意味着你不用再查看文档啦,编译器就可以帮你做类型检查,然后你就就快可以愉快地coding了!



例如,在Object-C的UIKit中, 在自定义的View里面处理触摸事件可以这么写:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
上述方法里面的set只可以保存UITouch实例, 因为文档里面就是这么说的。由于这个集合里面可以放任何对象,所以你需要在代码里面进行类型转换,也就是说把touches里面的对象转为UITouch对象。
当前Swift的标准库里面没有定义集合对象,但是你可以使用数组来代替集合对象,你可以用swift重写上面的代码:

func touchesBegan(touches: [UITouch]!, withEvent event: UIEvent!)
上面的代码明确告诉你 touches数组只可以包含 UITouch实例, 否则编译器就会报异常。这样一来,你就再不用去做那烦人的类型转换了,因为编译器给你做了类型安全检查,保证数组里面只允许有 UITouch对象。

简要说来,泛型为类提供了一个类型参数。所有的数组都有相同的作用,即按顺序储存变量的数值,泛型数组除了多了一个类型参数之外,没有其他的不同之处。或许这样想更容易理解:你将要应用在数组上的各种算法和储存的数值类型无关,因此这些算法对于泛型数组和非泛型数组都适用。
*/

func isEqualsInt(a:Int, b:Int) -> Bool {
    return (a == b)
}
func isEqualsDouble(a:Double, b:Double) -> Bool {
    return (a == b)
}
func isEqualsString(a:String, b:String) -> Bool {
    return (a == b)
}
/**
*  以上我们分别对3种不同的类型进行了比较,定义了类似的3个函数。那么我们是否可以定义1个函数能够比较3种不同的类型呢?如果isEqualsInt、isEqualsDouble和isEqualsString这3个函数名字后面的Int、Double和String是可变的,那么这些可变部分是与参数类型关联的。
*/


//func isEquals<T>(a: T, b: T) -> Bool {
//    return (a == b) //运算符是否要重载
//}
/**
它们必须遵守Comparable协议实现类型。Comparable协议表示可比较的,在Swift中,基本数据类型以及字符串都是遵守Comparable协议的。
*/
func isEquals<T: Comparable>(a: T, b: T) -> Bool {
    return (a == b)
}

/**
*  我们需要在T占位符后面添加冒号和协议类型,这种表示方式被称为泛型约束,它能够替换T的类型。在本例中,T的类型必须遵守Comparable协议的具体类。
*/
let n1 = 200
let n2 = 100


print(isEquals(n1, b: n2))


let s1 = "ABC1"
let s2 = "ABC1"


print(isEquals(s1, b: s2))
///


//--------------------------泛型栈---------------------------
//这里展示了如何写一个非泛型版本的栈,

//Int值型的栈:
struct IntStack {
    var items = [Int]()
    mutating func push(item: Int) {
        items.append(item)
    }
    mutating func pop() -> Int {
        return items.removeLast()
    }
}
/**
*  这个结构体在栈中使用一个Array性质的items存储值。Stack提供两个方法:push和pop,从栈中压进一个值和移除一个值。这些方法标记为可变的,因为它们需要修改(或转换)结构体的items数组。

上面所展现的IntStack类型只能用于Int值,不过,其对于定义一个泛型Stack类(可以处理任何类型值的栈)是非常有用的。
*/

//泛型的栈
struct TStack<T> {
    var items = [T]()
    mutating func push(item: T) {
        items.append(item)
    }
    mutating func pop() -> T {
        return items.removeLast()
    }
}
/**
*  T代替了实际Int类型。这种类型参数包含在一对尖括号里(<T>),紧随在结构体名字后面。

T定义了一个名为“某种类型T”的节点提供给后来用。这种将来类型可以在结构体的定义里任何地方表示为“T”。在这种情况下,T在如下三个地方被用作节点:

创建一个名为items的属性,使用空的T类型值数组对其进行初始化;
指定一个包含一个参数名为item的push方法,该参数必须是T类型;
指定一个pop方法的返回值,该返回值将是一个T类型值。
*/


/**
*  当创建一个新单例并初始化时, 通过用一对紧随在类型名后的尖括号里写出实际指定栈用到类型,创建一个Stack实例,同创建Array和Dictionary一样:
*/
var stackOfStrings = TStack<String>()
stackOfStrings.push("uno")
stackOfStrings.push("dos")
stackOfStrings.push("tres")
stackOfStrings.push("cuatro")
<script type="text/javascript"> $(function () { $('pre.prettyprint code').each(function () { var lines = $(this).text().split('\n').length; var $numbering = $('<ul/>').addClass('pre-numbering').hide(); $(this).addClass('has-numbering').parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($('<li/>').text(i)); }; $numbering.fadeIn(1700); }); }); </script>