Scala的泛型

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Scala的泛型相关的知识,希望对你有一定的参考价值。

Scala的泛型

泛型介绍:泛型用于指定方法或类可以接受任意类型参数,参数在实际使用时才被确定,泛型可以有效 地增强程序的适用性,使用泛型可以使得类或方法具有更强的通用性。泛型的典型应用场景 是集合及集合中的方法参数。
泛型方法:指定方法可以接受任意类型参数。
泛型类:指定类可以接受任意类型参数。
例:

object GenericTypeTest01 {
def main(args: Array[String]): Unit = {
println(new Student11[String,Int]("黄渤",33).name)
println(new Student12[String,Int]("zs",18).name)
println(new Student22[String,Int]("zs",8).age)
  }
}
class Student12[T,S](var name:T,var age:S){}
class Student11[T,S](var name:T, var age:S){}
class Person11[T](var name:T)
class Student22[T,S](name:T,var age:S) extends Person11(name) {}

 1. scala类型变量界定

  类型变量界定是指在泛型的基础上,对泛型的范围进行进一步的界定,从而缩小泛型的具体范围。
例:

object Test01 {
  def main(args: Array[String]): Unit = {
    var generic=new Generic
    val result: Int = generic.compare("aa","bb")
  }
}
class Generic{
  /**
    *T<:Comparable 表示compare方法中如果输入的类型处于Comparable类对应的继承体系中,则是合法的,否则编译不通过
    * 作用是:当调用泛型的方法是,但是不知道这个泛型是否有这个方法,此时使用泛型的类型变量界定,缩写范围,使得,泛型中有这个方法
    */
  def compare[T<:Comparable[T]](first:T,second:T)={
    val result=if(first.compareTo(second)>0) 1 else 0
    result
  }
}

 2. scala视图界定

  类型变量界定建立在类继承的层次上,但有时候这种限定不能满足实际要求,如果希望跨越类继承层次结构时,可以使用视图界定来实现,原理是通过隐式转换来实现。
  隐含参数和方法也可以定义隐式转换,称作视图。视图的绑定从另一个角度看就是 implicit 的转换。其实视图的绑定就是为了更方便的使用隐式转化,视图界定利用<%符号来实现。
例:

object Test01 {
  def main(args: Array[String]): Unit = {
    var ann1 = new Animals("zs", "18")
    var ann2 = new Animals("zs", 18)
  }
}
case class Animals[T, S <: Comparable[S]](var name: T, var age: S)

此时如果在[T, S <: Comparable[S]]使用<:的话,在运行的时候会报错
技术分享图片
因为:在类型变量界定的使用,默认的只能界定的类的是继承体系中的一员才成立,如果是使用隐式转换的方式默认不成立。上面代码:String类继承了Comparable,但是Int没有继承Comparable,只是隐式的将Int转换成了RichInt,RichInt继承了Comparable,所以此处出现了错误。
如果想隐式转换也可以通过泛型的话可以:

object Test01 {
  def main(args: Array[String]): Unit = {
    var ann1 = new Animals("zs", "18")
    var ann2 = new Animals("zs", 18)
  }
}
case class Animals[T, S <% Comparable[S]](var name: T, var age: 

利用<%号对泛型 S 进行限定,它的意思是 S 可以是 Comparable 类继承层次结构中实现了 Comparable 接口的类,也可以是能够经过隐式转换得到的实现了 Comparable 接口的类

 3. scala上界下界

  在指定泛型类型时,有时需要界定泛型类型的范围,而不是接收任意类型。比如,要求某个 泛型类型,必须是某个类的子类,这样在程序中就可以放心的调用父类的方法,程序才能正 常的使用与运行。此时,就可以使用上下边界 Bounds 的特性; Scala 的上下边界特性允许泛型类型是某个类的子类,或者是某个类的父类。
   U>:T这是类型下界的定义,也就是 U 必须是类型 T 的父类(或本身,自己也可以认为是自己的父 类)。
   S<:T这是类型上界的定义,也就是 S 必须是类型 T 的子类(或本身,自己也可以认为是自己的子 类)。
技术分享图片
上界

class Generic2{
//指定上界,T必须是Comparable的子类或者是Comparable
def compare[T<:Comparable[T]] (first:T,second:T) ={
  }
}

下界

class Generic3{
//指定下界,T必须是Comparable的父类或者是Comparable
def compare[T>:Comparable[T]] (first:T,second:T) ={
  }
}

 4. scala逆变和逆变

  • 协变
     协变定义形式如:trait List[+T]{}
     当类型 B 是类型 A 的子类型时,则 List[B]也可以认为是 List[A}的子类型,即 List[B]可以泛化 为 List[A]。也就是被参数化类型的泛化方向与参数类型的方向是一致的,所以称为协变 (covariance)
    技术分享图片
    例:
    object Test01 {
    def main(args: Array[String]): Unit = {
    var list1:MyList[Person]=new MyList(new Person)
    var list2:MyList[Son]=new MyList(new Son)
    list1=list2
    }
    }
    class Person
    class Son extends Person
    class MyList[+T](val value:T)

    这种方式,在list中只能将子类转化成为父类的list,如果在转变之后,这个list中也只能存放子类的类型对象。协变的性质:输入的类型必须是 Son 的超类。

  • 逆变
     逆变定义形式如:trait List[-T]{}
     当类型 B 是类型 A 的子类型,则 Queue[A]反过来可以认为是 Queue[B}的子类型。也就是被参数化类型的泛化方向与参数类型的方向是相反的,所以称为逆变(contravariance)。
    技术分享图片

  • 总结
    • 协变(covariant),如果它保持了子类型序关系≦。该序关系是:子类型≦基类型。
    • 逆变(contravariant),如果它逆转了子类型序关系。
    • 不变(invariant),如果上述两种均不适用。

以上是关于Scala的泛型的主要内容,如果未能解决你的问题,请参考以下文章

17.scala的泛型类

关于 Java 和 Scala 中的泛型

在 Scala 中使用带有 PureConfig 的泛型类型

如何接受在 Scala 中扩展类的泛型

通过给定的泛型类型 Scala 获取类的伴随对象

Scala编译器抱怨方法级别的泛型参数的类型不匹配