第13章 类型参数
13.1 泛型类
class Pair[T, S](val first: T, val second: S) { override def toString = "(" + first + "," + second + ")" }
//从构造参数推断类型 val p = new Pair(42, "String")
//设置类型 val p2 = new Pair[Any, Any](42, "String")
13.2 泛型函数
def getMiddle[T](a: Array[T]) = a(a.length / 2) // 从参数类型来推断类型 println(getMiddle(Array("Mary", "had", "a", "little", "lamb")).getClass.getTypeName) // 指定类型,并保存为具体的函数。 val f = getMiddle[String] _ println(f(Array("Mary", "had", "a", "little", "lamb")))
13.3 类型变量限
class Pair1[T <: Comparable[T]](val first: T, val second: T) { def smaller = if (first.compareTo(second) < 0) first else second } val p3 = new Pair1("Fred", "Brooks") println(p3.smaller)
class Pair2[T](val first: T, val second: T) { def replaceFirst[R >: T](newFirst: R) = new Pair2[R](newFirst, second) override def toString = "(" + first + "," + second + ")" }
13.4 视图界定
class PairSec04[T <% Comparable[T]](val first: T, val second: T) { def smaller = if (first.compareTo(second) < 0) first else second override def toString = "(" + first + "," + second + ")" } val p4 = new PairSec04(4, 2) // Works println(p4.smaller)
13.5 上下文界定
class Pair3[T : Ordering](val first: T, val second: T) { def smaller(implicit ord: Ordering[T]) ={ println(ord) if (ord.compare(first, second) < 0) first else second } override def toString = "(" + first + "," + second + ")" }
13.6 Manifest 上下文界定
def foo[T](x: List[T])(implicit m: Manifest[T]) = { println(m) if (m <:< manifest[String]) println("Hey, this list is full of strings") else println("Non-stringy list") } foo(List("one", "two")) // Hey, this list is full of strings foo(List(1, 2)) // Non-stringy list foo(List("one", 2)) // Non-stringy list
scala> class Foo{class Bar} defined class Foo scala> def m(f: Foo)(b: f.Bar)(implicit ev: Manifest[f.Bar]) = ev m: (f: Foo)(b: f.Bar)(implicit ev: Manifest[f.Bar])Manifest[f.Bar] scala> val f1 = new Foo;val b1 = new f1.Bar f1: Foo = Foo@67d18ed7 b1: f1.Bar = Foo$Bar@2c78d320 scala> val f2 = new Foo;val b2 = new f2.Bar f2: Foo = Foo@4b41dd5c b2: f2.Bar = Foo$Bar@3b96c42e scala> val ev1 = m(f1)(b1) ev1: Manifest[f1.Bar] = Foo@67d18ed7.type#Foo$Bar scala> val ev2 = m(f2)(b2) ev2: Manifest[f2.Bar] = Foo@4b41dd5c.type#Foo$Bar scala> ev1 == ev2 // they should be different, thus the result is wrong res0: Boolean = true
了解之后,我们总结一下,TypeTag到底有啥用呢?看下面的例子:
请留意:
=:=,意思为:type equality
<:< ,意思为:subtype relation
类型判断不要用 == 或 !=
class Animal{} class Dog extends Animal{} object MainFoo extends App{ override def main(args: Array[String]): Unit = { val list1 = List(1, 2, 3) val list2 = List("1", "2", "3") val list3 = List("1", "2", 3) def test1(x: List[Any]) = { x match { case list: List[Int] => "Int list" case list: List[String] => "String list" case list: List[Any] => "Any list" } } println(test1(list1)) //Int list println(test1(list2)) //Int list println(test1(list3)) //Int list import scala.reflect.runtime.universe._ def test2[A : TypeTag](x: List[A]) = typeOf[A] match { case t if t =:= typeOf[String] => "String List" case t if t <:< typeOf[Animal] => "Dog List" case t if t =:= typeOf[Int] => "Int List" } println(test2(List("string"))) //String List println(test2(List(new Dog))) //Dog List println(test2(List(1, 2))) //Int List } }
13.7 多重界定
不能同时有多个上界或下界,变通的方式是使用复合类型
T <: A with B
T >: A with B
可以同时有上界和下界,如
T >: A <: B
这种情况下界必须写在前边,上界写在后边,位置不能反。同时A要符合B的子类型,A与B不能是两个无关的类型。
可以同时有多个view bounds
T <% A <% B
这种情况要求必须同时存在 T=>A的隐式转换,和T=>B的隐式转换。
class A{} class B{} implicit def string2A(s:String) = new A implicit def string2B(s:String) = new B def foo2[ T <% A <% B](x:T) = println("foo2 OK") foo2("test")
可以同时有多个上下文界定
T : A : B
这种情况要求必须同时存在C[T]类型的隐式值,和D[T]类型的隐式值。
class C[T]; class D[T]; implicit val c = new C[Int] implicit val d = new D[Int] def foo3[ T : C : D ](i:T) = println("foo3 OK") foo3(2)
13.8 类型约束
类型约束,提供了限定类型的另一种方式,一共有3中关系声明:
T =:= U意思为:T类型是否等于U类型
T <:< U意思为:T类型是否为U或U的子类型
T <%< U 意思为:T 类型是否被隐式(视图)转化为 U
如果想使用上面的约束,需要添加“隐式类型证明参数” 比如:
class Pair5[T] (val first: T, val second: T)(implicit ev: T <:< Comparable[T]){}
使用举例:
import java.io.File class Pair6[T](val first: T, val second: T) { def smaller(implicit ev: T <:< Ordered[T]) = { if(first < second) first else second } } object Main6 extends App{ override def main(args: Array[String]): Unit = { //构造Pair6[File]时,注意此时是不会报错的 val p6 = new Pair6[File](new File(""), new File("")) //这就报错了 p6.smaller } }
13.9 型变
术语:
英文 | 中文 | 示例 |
Variance | 型变 | Function[-T, +R] |
Nonvariant | 不变 | Array[A] |
Covariant | 协变 | Supplier[+A] |
Contravariant | 逆变 | Consumer[-A] |
Immutable | 不可变 | String |
Mutable | 可变 | StringBuilder |
其中,Mutable常常意味着Nonvariant,但是Noncovariant与Mutable分别表示两个不同的范畴。
即:可变的,一般意味着“不可型变”,但是“不可协变”和可变的,分别表示两个不同范畴。
型变(Variance)拥有三种基本形态:协变(Covariant), 逆变(Contravariant), 不变(Nonconviant),可以形式化地描述为:
一般地,假设类型C[T]持有类型参数T;给定两个类型A和B,如果满足A <: B,则C[A]与 C[B]之间存在三种关系:
如果C[A] <: C[B],那么C是协变的(Covariant); 如果C[A] :> C[B],那么C是逆变的(Contravariant); 否则,C是不变的(Nonvariant)。
Scala的类型参数使用+标识“协变”,-标识“逆变”,而不带任何标识的表示“不变”(Nonvariable):
trait C[+A] // C is covariant
trait C[-A] // C is contravariant
trait C[A] // C is nonvariant
如何判断一个类型是否有型变能力:
一般地,“不可变的”(Immutable)类型意味着“型变”(Variant),而“可变的”(Mutable)意味着“不变”(Nonvariant)。 其中,对于不可变的(Immutable)类型C[T] 如果它是一个生产者,其类型参数应该是协变的,即C[+T]; 如果它是一个消费者,其类型参数应该是逆变的,即C[-T]。
13.10 协变和逆变点
class A[+T] { def func(x: T) {} }
covariant type T occurs in contravariant position in type T of value x
class A[-T] { def func(x: T) {} }
class A[+T] { def func(): T = { null.asInstanceOf[T] } }