scala泛型

泛型类

class GenericClass[S,T](val s:S, val t:T) {
}

// 实例化方式1
val generic = new GenericClass[String, Int]("s", 1)
// 实例化方式2:类型推导
val generic = new GenericClass("s", 1)

泛型方法

class GenericClass[S,T](val s:S, val t:T) {
  //参数T与类定义的T无关
  def getData[T](list:List[T]) = list(list.length/2)
}

val generic = new GenericClass("s", 1)
//方式1
println(generic.getData[Int](List(1,2,3))) //2
//方式2:类型推导
println(generic.getData(List(1,2,3))) //2
//方式3:获得getData的引用,必须指定泛型类型
val f=generic.getData[Int]_
println(f(List(1,2,3))) //2

类型变量界定(Type Variable Bound)

// 编译错误  
def typeVariable[T](a: T, b: T) = {  
    if (a.compareTo(b) > 0) 1  
    else -1  
}  

// 通过编译,为范型T增加了约束,限定上界为Comparable[T]
// 范型类型T也就具有了Comparable[T]中的compareTo(T)方法,类似于java中的继承 
def typeVariable[T <: Comparable[T]](a: T, b: T) = {  
    if (a.compareTo(b) > 0) 1  
    else -1  
}
// 正确运行,String有上界Comparable[T]
println(typeVariable("scala","java"))
// 运行错误,Int没有上界Comparable[T],但是为何编译不报错?
// 如果想成功运行的话,就需要进行一次隐式转换,将Int类型转换成支持Comparable[T]的类型,此时应该改用视图界定<%
// 视图界定帮我们进行了隐式转换,将Int转换成了支持Comparable[T]的RichInt类型
println(typeVariable(1,2))

视图界定(View Bound)

// <% 泛型视图界定符,表示把传入不是Comparable[T]类型的 隐式传换 为Comparable[T]类型 
class PairNotPerfect[T <% Comparable[T]](val first: T, val second: T) {
  // first之所以能使用方法compareTo,是因为first 被隐式传换 为Comparable[T]类型
  def bigger = if (first.compareTo(second) > 0) first else second
}
val pair = new PairNotPerfect(1, 2)
println(pair.bigger)

上下文界定(Context Bound)

上下文界定是隐式参数的语法糖。类型参数形式为T:M的形式,其中M是一个泛型类,这种形式要求存在一个M[T]类型的隐式值

class Person(val age: Int) {  
    println("person==> " + age)  
}  

// PersonOrdering继承了Ordering[T],而Ordering[T]又继承了Comporator[T],所以下面方法中有compare(x: T, y: T)方法  
class PersonOrdering extends Ordering[Person] {  
  override def compare(x: Person, y: Person): Int = {  
    if (x.age > y.age) 1 else -1  
  }  
}  

// 该类定义了一个上下文界定,意思是在其作用域内,必须有一个Ordering[T]的隐式值,而这个隐式值可以作用于内部的方法  
class Pair[T: Ordering](val first: T, val second: T) {  
  // 该方法需要一个类型为Ordering[T]的隐式参数  
  def old(implicit ord: Ordering[T]) = {  
    if (ord.compare(first, second) > 0) first else second  
  }  
}  

// 定义一个隐式值,类型为Ordering[T]  
implicit val po = new PersonOrdering  
val p = new Pair(new Person(18), new Person(19))  
// 调用old方法时,不需要传入参数,根据我们的上下文界定要求,po满足要求,因此作为参数传入old  
println(p.old.age) // 19 

Manifest关键字

Manifest是scala2.8引入的一个特质,用于编译器在运行时也能获取泛型类型的信息。
在JVM上,泛型参数类型T在运行时是被“擦拭”掉的,编译器把T当作Object来对待,所以T的具体信息是无法得到的;为了使得在运行时得到T的信息,scala需要额外通过Manifest来存储T的信息,并作为参数用在方法的运行时上下文。

def test[T](x: T, m: Manifest[T]) {
}

上述的方法要求调用者要额外传入m参数,非常不友好,好在scala中有隐式转换、隐式参数的功能,在这个地方可以用隐式参数来减轻调用者的麻烦

def foo[T](x: List[T])(implicit m: Manifest[T]) = {
    //manifest定义在Predef中,直接使用即可
    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

不过上面的foo 方法定义使用隐式参数的方式,仍显得啰嗦,于是scala里又引入了“上下文界定“(通常Manifest会以隐式参数和上下文绑定的形式使用),可以简化为:

def foo[T : Manifest] (x : List[T])

这个机制起因是scala2.8对数组的重新设计而引入的,原本只是为了解决数组的问题,后续被用在更多方面

def arrayMake[T: Manifest](first: T, second: T) = {
    //数组在声明时必须要求指定具体的类型,所以泛型要用Manifest
    val r = new Array[T](2)
    r(0) = first
    r(1) = second
    r
}

arrayMake(1,2).foreach(println) //1 2

scala在2.10里却用TypeTag替代了Manifest,用ClassTag替代了ClassManifest,原因是在路径依赖类型中,Manifest拿到的信息是不精确的

ClassTag关键字

替代ClassManifest
保存了泛型擦除后的原始类型T,运行时指定在编译的时候无法指定的类型信息

// 编译器会翻译成implicit,因为ClassTag中间有隐式参数和隐式值
def mkArray[T: ClassTag](elems:T*) = Array[T](elems:_*)
//mkArray: mkArray[T](val elems: T*)(implicit <synthetic> val evidence$2: scala.reflect.ClassTag[T]) => Array[T]
//这时候ClassTag就可以把Int类型这个信息传递给编译器
mkArray(10,20).foreach(println) //10  20
//res0: Array[Int] = [I@7b9cc588

多重界定

   /* 
      // 表示:A和B为T上界 
      T <: A with B 

      // 表示:A和B为T下界 
      T >: A with B 

      // 表示:同时拥有上界和下界,并且A为下界,B为上界,A为B的子类,顺序不能颠倒。 
      T >: A <: B 

      // 表示:类型变量界定,即同时满足AT这种隐式值和BT这种隐式值 
      T:A:B 

      // 表示:视图界定,即同时能够满足隐式转换的A和隐式转换的B 
      T <% A <% B  
    */  

类型约束

  // A =:= B   // 表示A类型等同于B类型  
  // A <:< B   // 表示A类型是B类型的子类  
  def rocky[T](i: T)(implicit ev: T <:< java.io.Serializable) {  
    println("...")
  }  
  rocky("Spark") 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值