Scala学习笔记(四)----泛型与隐式转换

泛型

  • 泛型类
  • 泛型函数
  • trait可以泛型
  • object不能泛型
class B[S,T](val b1 : S,val b2 : T)
def print[A](content : A){println(content)}
val m = Map[Int,String](1 -> "a")
trait X[T]

上界和下界

用上界来表示前面的前面的泛型是后面的泛型的子类型。例如下面的例子,如果没有给泛型T加上泛型上界 Comparable[T],则程序会出错,因为scala不能确定泛型T有compareTo方法。

用下界来表示R必须是T的超类。

视图上界跟上界类似,但是它允许类型的隐式转换,例如,在下面的例子里,如果 val p = new Pair(1,2),则程序依然不能正常运行,因为scala的Int类型没有实现Comparable接口,而RichInt实现了,因此,如果使用视图上界,Int会隐式转换RichInt。可以认为视图上界是更广泛的上界。

上下文界定,T<%V 表示T到V的隐式转换,而上下文转换[T:M]表示必须存在一个类型为M[T]的隐式值。

  • Upper bounds
    T <: Comparable[T]
  • Lower bounds
    R >: T
  • View bounds
    T <% Comparable[T]
  • Context bounds
object Bounds extends App{
    val p = new Pair("A","B")
    println(p.smaller)  //A

    val s1 = new Student("A")
    val s2 = new Student("B")
    val pair1 = new Pair(s1,s2)
    val p1 = new Person("AA")
    pair1.replaceFirst(p1)
}

class Pair[T <: Comparable[T]](val first : T,val second : T){
   def smaller = if (first.compareTo(second)<0) first else second

    def replaceFirst[R >: T](newFirst : R) = new Pair(newFirst,second)
}

class Person(val name : String)

class Student(name : String) extends Person(name)

类型约束

  • T =:= U T是不是跟U类型相同。
  • T <:< U T是不是U的子类型。
  • T <%< U T是不是能隐式转换到U。

型变

  • 型变
  • 协变+ class X[+T](val x : T){} 协变是用来做返回值的。
  • 逆变— class X[-T](val x : Int){} 逆变是用来做参数泛型的。
class X[-T](val y : Int){
  def x(t: T){
    println(t)
  }
}
new X(1).x("xxxx")
new X[Int](1).x(1)

类型通配符

类似于java中的<? extends A>,在scala中的形式是:[_ <: A]

隐式转换implicit conversion

  • 为现有的类库增加功能 enhance current library
  • 位于源或者目标类型的companion object 中的隐式函数
  • 位于当前作用域可以以单个标示符指代的隐式函数
object ImplicitDemo2 extends App{
    import Context2.file2RichFile
    //import Context2._
    new File("D:/code/scala/week2/data.txt").read
}

class RichFile2(val file : File){
  def read = Source.fromFile(file.getPath).mkString

}

object Context2{
  implicit def file2RichFile(f: File) = new RichFile2(f)
}

上面的例子中,new File("D:/code/scala/week2/data.txt")对象没有read函数,但是因为引入了Context2.file2RichFile,这个函数可以将File类型的对象隐式转换成RichFile2对象,而RichFile2是有read方法的。

  • 如果代码能在不使用隐式转换的前提下通过编译则不会使用隐式转换
  • 编译器不会同时使用多个隐式转换
  • 存在二义性的隐式转换不被允许
object ImplicitDemo2 extends App{
    import Context2._
    AAAA.print("Jack")("Hello")//打印Hello:Jack
    AAAA.print("Jack")//也会打印Hello:Jack
}

object AAAA{
  def print(content : String)(implicit prefix : String){
    println(prefix + " : " +content)
  }
}

object Context2{
  implicit val ccc : String = "Hello"
}

上面的例子中,只要是在import Context2._的作用域中,所有的需要隐式转换的字符串都会被赋值为object Context2下的ccc值。这里发生转换的两个要素是:一是函数将参数声明为隐式类型implicit prefix : String,二是伴生对象提供了字符串隐式转换。

  • 隐式参数
def smaller[T](a : T,b : T) = if (a<b)a else b

上面的函数会出错,因为scala不能确定T类型是否能使用<操作符。前面我们讲到,可以使用上界 <: Comparable[T]来实现,而更一般的做法是,使用隐式参数转换来实现:

def smaller[T](a : T,b : T)(implicit order : T => Ordered[T]) = if (a<b)a else b

scala会首先找对象a里面是否可以使用<操作符,如果不能,再往回找,看范围里是否有隐式转换,如果有隐式参数,会将a隐式转换成Ordered[T]类型。

  • 隐式类implicit class
  • 必须定义在另一个class/object/trait里面
  • 构造器中只能带一个不是implicit类型的参数
  • 作用域中不能与隐式类类名相同的成员变量,函数名及object名称
object ImplicitDemo2 extends App{
    import Context2._
    println(1.add2)   //3
}
object Context2{
  implicit class HH(x : Int){
    def add2 = x+2
  }
}

上面的隐式类会将Int类型隐式转换成HH,从而可以调用类HH的方法。

  • 再看context bounds
object ImplicitDemo2 extends App{

    import Context2._
    val p1 = new Pair3(1,2)
    val p2 = new Pair3("A","B")
    println(p1.smaller)
    println(p2.smaller)

}

class Pair3[T : Ordering](val first : T ,val second : T){
  def smaller(implicit ord : Ordering[T]) = 
    if(ord.compare(first, second) < 0) first else second
}

不管我们是用Int或是String去生成Pair3对象时,我们都能得到正确的结果。因为隐式转换。如果我们有一个自定义的类,又想使用Ordering/Ordered这种trait时。可以通过下面的方式来实现。

object ImplicitDemo2 extends App{
    implicit object Line extends LineOrdering
    //将Line隐式转换成LineOrdering类型

    val l1 = new Line(1)
    val l2 = new Line(2)
    val p3 = new Pair3[Line](l1,l2)
    println(p3.smaller) 
}

class Pair3[T : Ordering](val first : T ,val second : T){
  def smaller(implicit ord : Ordering[T]) = 
    if(ord.compare(first, second) < 0) first else second
}

trait LineOrdering extends Ordering[Line]{//LineOrdering实际上就是 Ordering[Line]
  override def compare(x : Line , y : Line)={  //重写compare函数
    if(x.len < y.len ) -1
    else if (x.len  == y.len )0
    else 1
  }
}

class Line(val len : Double){
  override def toString() = "Length of line : "+len
}

Comments !