泛型类
在类声明时, 定义一些泛型类型, 然后在类的内部, 就可以使用这些泛型类型
在需要对类中的某些成员, 如字段或方法中的参数进行统一的类型限制时, 可以使用泛型类, 使得程序具有更好的健壮性和稳定性
在使用类的时候, 将类型参数替换为实际的类型即可
scala 会自动推断泛型类型: 给泛型类型的字段赋值时, scala 会自动对类型进行推断
泛型函数:
与泛型类相似, 在声明函数时指定泛型类型, 然后在函数体内, 多个变量或返回值, 就可以使用泛型类型进行声明.
可以通过给使用了泛型类型的变量传递值, 让 scala 自动推断泛型的实际类型, 也可以在调用函数的时候, 手动指定泛型的实际类型
- class Triple[X, Y, Z](val first: X, val second: Y, val thrid: Z)
- object Hello_Type_Parameterization {
- def main(args: Array[String]): Unit = {
- // 在定义后 scala 的类型推断会得出 triple 类型为 Triple[String, Int, Double]
- val triple = new Triple("Spark", 3, 3.1415926)
- // 显示声明类型
- val bigData = new Triple[String, String, Char]("Spark", "Hadoop", 'R')
- // 定义泛型函数
- def getData[T](list: List[T]) = list(list.length / 2)
- println(getData(List("Spark", "Hadoop", 'R'))) //Hadoop
- // 显式指定类型
- val f = getData[Int] _ //val f: List[Int] => Int
- println(f(List(1,2,3,4,5,6,7,8))) //5
- // 定义参数也存在上下文的约束
- def foo[A, B](f: A => List[A], b: A) = f(b)
- }
- }
类型变量的边界(Bounds)
<: : 指明上界, 表达了泛型的类型必须是 "某种类型" 或某种类型的 "子类"
'>: : 指明下界, 表达了泛型的类型必须是" 某种类型 "或某种类型的" 父类 "
- /**
- * 这里的 [T <: Comparable[T]] 表示类型 T 必须是 Comparable[T] 的子类
- * 如果 T 为 Comparable[T]的子类了, 那么 T 一定会有 compareTo 这个方法, 这是一个 java 的方法
- */
- class Pair[T <: Comparable[T]](val first: T, val second: T) {
- def bigger = if(first.compareTo(second)> 0) first else second
- }
- object Type_Variable_Bounds {
- def main(args: Array[String]): Unit = {
- val pair = new Pair("Spark", "Hadoop")
- println(pair.bigger) //Spark
- }
- }
视图界定 View Bounds
view bounds 其实就是 bounds 上边界的加强版本, 对 bounds 的补充 <变成<%
可以利用 implicit 隐式转换将实参类型转换成目标类型
- /**
- 当给下面这个类传入 3,5 时会报错. 因为 3,5 不是 Comparable[T]的子类
- class Pair_NotPerfect[T <: Comparable[T]](val first: T, val second: T) {
- def bigger = if (first.compareTo(second)> 0) first else second
- */
- /*
- * 将<: 改成 <% 就是视图界定 这样就可以传递 3 和 5 了, 就不会报错了
- * 我们可以把传入的 T 类型的实例隐式的转换成 Comparable[T]类型
- */
- class Pair_NotPerfect[T <% Comparable[T]](val first: T, val second: T) {
- def bigger = if (first.compareTo(second)> 0) first else second
- }
Ordered 视图界定
- /**
- *
- * 上面这种方式的 12 行 first.compareTo(second)> 0 通过 compareTo 来比较 但是不能直观的像数学比较那样清晰
- * Scala 提供了 Ordered 视图界定, Ordered 在 Comparable 上提供一些关系型的操作符 <> <=>= 等
- *
- */
- class Pair_Batter[T <% Ordered[T]](val first: T, val second: T) {
- // 这里的> 是因为 Ordered 中提供的方法
- def bigger = if (first> second) first else second
- }
- object View_Bounds {
- def main(args: Array[String]): Unit = {
- var pair = new Pair_NotPerfect("Spark", "Hadoop")
- println(pair.bigger) //Spark
- /*
- * 当类型界定为 Pair_NotPerfect[T <: Comparable[T]]报错 因为 Int 本身不是 Comparable 的子类
- *
- * 当类型界定为视图界定时 Pair_NotPerfect[T <% Comparable[T]] 就可以正常运行
- * 是因为 Int 本身不是 Comparable 的子类型 Scala 通过 "隐式转换" 将 Int 转换成 RichInt 而这个类型是 Comparable 的子类
- */
- var pairInt = new Pair_NotPerfect(3, 5) //Int -> RichInt
- println(pairInt.bigger) //5
- /**
- * 注意: 这样定义不是因为 String 的上界是 Ordered[String]
- * 当使用视图界定时 会发生 "隐式转换" 把 String --> RichString
- * 而 RichString 是 Ordered[RichString]的子类型 RichString 中是实现了这样的 <> <=>= 等方法
- * 从而真正是让 String 类型完成视图界定
- */
- var pair_Batter_String = new Pair_Batter("Java", "Scala")
- println(pair_Batter_String.bigger) //Scala
- val pair_Batter_Int = new Pair_Batter(20, 12)
- println(pair_Batter_Int.bigger) //20
- }
- }
上下文界定 Context Bounds
上下文界定[T : Ordering], 这种写法在 Spark 中是广泛使用的, 说明存在一个隐式的值 Ordering[T]
- implicit ordered: Ordering[T]
- class Pair_Ordering[T : Ordering] (val first: T, val second: T) {
- // 这是一个隐式转换的显式定义, 这个函数没有参数, 当时函数执行的时候 这个隐式值就会自动传进来
- def bigger(implicit ordered: Ordering[T]) = {
- if (ordered.compare(first, second)> 0) first else second
- }
- }
- object Context_Bounds {
- def main(args: Array[String]): Unit = {
- val pair = new Pair_Ordering("Spark", "Hadoop")
- println(pair.bigger) //Spark
- val pairInt = new Pair_Ordering(3, 5)
- println(pairInt.bigger) //5
- }
- }
ClassTag 和 Manifest
上下文界定[T : ClassTag]: 相当于动态类型, 记录了当前 T 的类型, 你使用时传入什么类型就是什么类型, 在实际运行的时候我们获取 T 具体的类型
主要是应用于创建泛型数组, 因为数组必须有具体的类型, 否则无法创建相应的数组, 利用 [T : ClassTag] 就可以创建成功
- import scala.reflect.ClassTag
- object Manifest_ClassTag {
- def main(args: Array[String]): Unit = {
- /**
- * Q: 可以创建泛型数组吗?
- * 理论上是不可以的, 因为没有指定具体的, 在 Scala 程序运行中, 数组必须有具体的类型, 没有否无法创建的相应的数组
- * 引出 Manifest 的概念可以创建泛型数组
- *[T : Manifest]这样的写法被称之为 Manifest 上下文界定 实质上这是需要一个 Manifest[T]类型的隐式对象 这又是一个 "隐式转换" 的过程, 有这样的一个隐式转换来辅助我们构建 Manifest[T]来确定 T 的类型
- * 通过这个隐式的值来辅助构建泛型数组, 来确定 T 的具体类型
- * 所以在创建泛型函数时 需要 Manifest 的类型来辅助构建泛型数组, 借助 Manifest 类型对象来指定泛型数组具体的类型
- *
- * 通过 Manifest[T]可以记录 T 的类型 在实际运行的时候我们获取 T 具体的类型
- * */
- def arrayMake[T : Manifest](first: T, second: T) = {
- val r = new Array[T](2)
- r(0) = first
- r(1) = second
- r
- }
- arrayMake(1, 2).foreach(println) //1 2
- /**
- * Manifest 的原生写法 不推荐
- */
- def manif[T](x: List[T])(implicit m: Manifest[T]) = {
- if (m <:<manifest[String]) //<:< 表示 m 是 manifest[String]类型
- println("List Strings")
- else
- println("Some other type")
- }
- manif(List("Spark", "Hadoop")) //List Strings
- manif(List(1, 2)) //Some other type
- manif(List("Scala", 3)) //Some other type
- /**
- * [T : ClassTag]这种写法说明: 当这个函数在运行时时 对存在一个 ClassTag[T]一个隐式值 这种方式是最常用的
- 主要是在运行时指定, 在编译时无法确定的 type 的信息
- 编写编译的时候没有具体类型, 运行的时候必须要有具体的类型, 所以需要一种机制运行的时候会根据类型进行推断类型, classTag 会帮我们存储这个类的信息, 然后交给虚拟机
- */
- def mkArray[T : ClassTag](elems: T*) = Array[T](elems: _*)
- mkArray(42, 13).foreach(println) //42 13
- mkArray("Japan", "Brazil", "Germany").foreach(println) //"Japan", "Brazil", "Germany"
- }
- }
Scala 多重界定
T <: A with B:T 是 A 或者 B 的子类
T>: A with B:A 或者 B 是 T 的子类
T>: A <: B:T 同时拥有下界 A 和上界 B(也就是说 A 必为 B 的子类型, 下界必须写在前面, 上界必须写在后面)
T : A : B : 上下文界定
T <% A <% B: 视图界定, T 可以同时拥有多个视图界定, 必须能够同时转化为 A 和 B 的要求
Scala 类型约束
- /**
- * A =:= B 表示 A 类型等同于 B 类型
- * A <:<B 表示 A 类型是 B 类型的子类型
- */
- object Type_Contraints {
- def main(args: Array[String]): Unit = {
- // 隐式参数是从哪里传进来的? 后面有一些列的判断
- def rocky[T](i: T)(implicit ev: T <:< java.io.Serializable) {
- println(ev) //<function1>
- println("Life is short, you need Spark!")
- }
- rocky("spark")
- //rocky(100) error:Cannot prove that Int <:<java.io.Serializable.
- }
- }
总结
边界(Bounds)
[T <: Comparable[T]]
<: : 指明上界, 表达了泛型的类型必须是 "某种类型" 或某种类型的 "子类"
'>: : 指明下界, 表达了泛型的类型必须是" 某种类型 "或某种类型的" 父类 "
视图界定 View Bounds
[T <% Comparable[T]]
<% : 对上边界的加强版, 可以利用 implicit 隐式转换将实参类型转换成目标类型
上下文界定 Context Bounds
[T : Ordering]
说明存在一个隐式的值 Ordering[T](implicit ordered: Ordering[T])
针对创建泛型数组的上下文界定:
- [T : ClassTag]
- [T : Manifest]
来源: http://www.jianshu.com/p/caca1ba8976e