美文网首页
详解 Scala 中的 Context Bounds

详解 Scala 中的 Context Bounds

作者: 枫叶_huazhe | 来源:发表于2019-05-17 17:55 被阅读0次

    What is Context Bounds?

    这是Scala 2.8 引入的新特性,通常与类型类模式(type class pattern)一起使用。一个 Context Bounds 需要一个参数类型,比如 Ordered[A],一个 Context Bounds 可以描述为一个隐式值。比如对于一个类型 A,它需要有一个 B[A] 的隐式值存在。

    def foo[A : B](a: A) = g(a) 
    

    上面的定义中,函数 g(a) 需要传入一个隐式值 B[A],即上面的 foo 函数签名等价于如下模式

    def foo[A](a:A)(implicit b:B[A]) = g(a)
    

    简单说 Context Bounds 算是一个隐式参数的语法糖,可以简写代码。

    使用举例

    下面我们定义一个方法 foo,它拥有参数类型T,并且需要传入隐式对象 Stringer, 用于对 foo里的两个参数进行 toString 操作,代码如下:

    object ContextBoundSpec {
    
        def foo[T](a: T, b: T)(implicit stringer: Stringer[T]): String = {
            stringer.toString(a, b)
        }
    }
    
    trait Stringer[T] {
        def toString(a: T, b: T): String
    }
    

    相信大家对上面代码没有任何疑问,现在我们将使用 Context Bounds 语法糖的形式修改 foo 函数

    object ContextBoundSpec {
       def foo[T:Stringer](a: T, b: T): String = {
            val stringer = implicitly[Stringer[T]]
            stringer.toString(a, b)
        }
    }
    

    注意到,通过新的模式写法后,两个函数是等价的,但是下面这个函数由于通过 context bound 形式后,在方法入参上拿不到关于 Stringer 对象的值,那么我们可以通过 implicitly 这个 标识符 来获取程序上下文中存在的关于Stringer[T]类型的隐式值,这个 标识符 的作用就在于此,它是自动的去获取到。

    • 下面是测试例子:
     def main(args: Array[String]): Unit = {
         implicit val stringer: Stringer[Int] = new Stringer[Int]() {
             override def toString(a: Int, b: Int): String = {
                    s"$a-$b"
             }
         }
        val result1 = foo1(2, 3)
        val result2 = foo2(2, 3)
    
        println(result1)
        println(result2)
     }
    

    通过实例化一个Int类型的 Stringer,并且分别调用 foo1foo2 结果完全一致。

    使用场景

    上下文边界主要用于所谓的类型类模. 基本上,此模式通过一种隐式适配器模式使功能可用来实现继承的替代方法。

    在Scala 源码和部分三方库的源码中大量使用了 Context Bounds 这种模式,所以我们需要清楚的认识到这种写法,并灵活应用。

    推荐

    更深层的理解 Scala 上下文绑定和其复杂的类型系统,可以参考 Scala老司机 老王的这篇文章

    相关文章

      网友评论

          本文标题:详解 Scala 中的 Context Bounds

          本文链接:https://www.haomeiwen.com/subject/ivboaqtx.html