admin 管理员组文章数量: 887031
scala 上界 <:, 下界>:, 视界 <%, 边界 :, 协变 +T, 逆变
概述
上界 (<:) 下界(>:) 是类型约束范畴,用来约束对象的子类是谁,父类是谁
协变 (+T) 逆变(-T)是泛型范畴,主要用于集合类型变量赋值。
视界 (<%) 边界( :)是隐式调用的简写。
上界 (<:) 下界(>:)
上界:要求对象必须是某一个对象的子类,因为子类会继承父类的属性和方法,所以父类的方法和属性这路都可以用。
trait A {def func()}class B extends A {override def func(): Unit = {println("i am b")}}class C extends A {override def func(): Unit = {println("i am c")}}def test[T <: A](obj: T): Unit = {obj.func()}test(new B()) // i am btest(new C()) // i am c
下界:要求对象必须是某一个对象的父类。主要用在已知某一个具体类型,但是要给它赋值给另一个变量,该变量必须是具体类型的父类型才行。
举例:
对于下面的例子,我们要把元素B放到一个集合中,这个集合必须是类型B父类容器才行。这里就是B是已知类型,集合类型未知。
trait A {def func()}class B extends A {override def func(): Unit = {println("i am b")}}def add[T >: B](list: collection.mutable.Set[T], elem: B): collection.mutable.Set[T] = {list.add(elem)list}
协变 (+T) 逆变(-T)
逆变协变指的是类型T和包装类型直接的关系。
有两个类如下所示:
trait A {def func()}class B extends A {override def func(): Unit = {println("i am b")}}
假设有个包装类型F[T]
class F[T]
现在我们生产一个F[B]实例,然后赋值给F[A]类型的变量.就会报错。因为F[B]并不是F[A]的子类。
val a:F[A] = new F[B]() // error
所以这个时候才会需要逆变和协变。
协变:子类的包装类型也是父类包装类型的子类
class F[+T] // 协变val a: F[A] = new F[B]() // 正确
逆变:子类的包装类型是父类包装类型的父类
class G[-T] // 逆变val b: G[B] = new G[A]() // 正确
一个比较经典的例子是:scala 函数的定义.scala中所有的函数,参数都是逆变的,返回值都是协变的。 trait Function[-T1,+T2]
.也就是说一个函数要赋值给一个函数类型变量。这个函数的参数必须是函数变量参数的父类,返回值必须是函数变量返回值的子类。
trait A {def func()}class B extends A {override def func(): Unit = {println("i am b")}}class C extends B {}val func: B => B = (a: A) => { new C() } // 把 A=>C类型的函数赋值给B=>B类型的变量
视界 <%, 边界 :
视图和边界看到的比较多,但是功能比较强大,也容易被误解。比如 ( : )就经常会被误以为是上界和下届的一种。
视界 <%
视界只是一种隐式类型函数的简写。如果我们需要一个Fruits类型的隐式参数,但是我们只有一个 T 类型,这时就可以用视界。他要求想使用这个函数,必须提供一个由T => Fruits
的隐式调用。 比如下面:
scala> case class Fruits(name: String)
defined class Fruitsscala> def getFruits[T <% Fruits](value: T): Fruits = value
getFruits: [T](value: T)(implicit evidence$1: T => Fruits)Fruits等价于scala> def getFruits[T](value: T)(implicit evidence: T => Fruits): Fruits = evidence(value)
getFruits: [T](value: T)(implicit evidence: T => Fruits)Fruits
边界 :
边界相对来将应用场景比较多一些。下面有个函数anothr,需要一个隐式参数。我们在func中调用了这个方法,但是我们并没有提供隐式参数。这就和视图有点类似了。在视图中要求传入一个隐式函数,但这里是要求传入一个隐式类型。
class F[T]def another[T](elem: T)(implicit f: F[T]) = {// do nothing
}def func[T: F](elem: T): Unit = {another(elem)
}scala> def func[T: F](elem: T): Unit = {another(elem)}func: [T](elem: T)(implicit evidence$1: F[T])Unit
// 使用
scala> func(1)(new F[Int]())
或则
scala> func(1)(new F()) //自行推断
有一个比较经典的例子,利用这个方法可以实现隐式泛型
import scala.reflect.runtime.universe._typeOf函数需要传入一个隐式类型TypeTag[T]
// def typeOf[T](implicit ttag: TypeTag[T]): Type = ttag.tpedef getTypeTag[T: TypeTag](obj: T) = typeOf[T] // 我们调用的时候也没有传入隐式类型,因为scala运行环境中会自定存储。
getTypeTag(1)
本文标签: scala 上界 < 下界> 视界 < 边界 协变 T
版权声明:本文标题:scala 上界 <:, 下界>:, 视界 <%, 边界 :, 协变 +T, 逆变 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.freenas.com.cn/jishu/1688372831h212100.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论