scala 上界 <:, 下界>:, 视界 <%, 边界 :, 协变 +T, 逆变-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 b
test(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 Fruits
scala> 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.tpe
def getTypeTag[T: TypeTag](obj: T) = typeOf[T]
// 我们调用的时候也没有传入隐式类型,因为scala运行环境中会自定存储。
getTypeTag(1)
上一篇: Vue入门知识
下一篇: vue路由跳转传参数