scala 上下文界定
一. 传统隐式转换实现
先看一个传统的隐式转换方法实现
/** 上下文界定* author: leisu* date 2018/8/15 17:36*/
object ContextBounds extends App {implicit def ageCondition(person: Poet) = person.age > 18val cb = new ContextBoundsval list: List[Poet] = List(new Poet("李白", 35),new Poet("白居易", 19),new Poet("苏轼", 34),new Poet("辛弃疾", 17),new Poet("杜甫", 21),new Poet("张良", 18))println(cb.doFilter(list))
}class ContextBounds {/*** 根据传入的data和判断条件con,进行数据过滤,并返回* 其中T是混入了Condition[T]的子类*/def doFilter[T <: Condition[T]](data: List[T])(implicit condition: T => Boolean): List[T] = data.filter(_.judge(condition))
}/*** 定义一个Condition特质,包含condition方法,混入此特质的类,可以使用condition进行筛选** @tparam T 协变*/
trait Condition[+T] {def judge(condition: T => Boolean): Boolean
}/*** 诗人类*/
case class Poet(val name: String, val age: Int) extends Condition[Poet] {//复写方法,调用con函数,传入thisoverride def judge(condition: Poet => Boolean): Boolean = condition(this)
}
可以看到,隐式传入的参数是: ageCondition,这是一个def方法,类型为(Poet => Boolean),是((T<: Condition)=> Boolean)的子类,所以可以隐式传入。在调用时,只显式传入类第一个参数,具体调用为:cb.doFilter(list),就可以成功。
调用后的输出为:
/** 上下文界定* author: leisu* date 2018/8/15 17:36*/
object ContextBounds extends App {implicit def ageCondition(person: Poet) = person.age > 18val cb = new ContextBoundsval list: List[Poet] = List(new Poet("李白", 35),new Poet("白居易", 19),new Poet("苏轼", 34),new Poet("辛弃疾", 17),new Poet("杜甫", 21),new Poet("张良", 18))println(cb.doFilter(list))
}class ContextBounds {/*** 根据传入的data和判断条件con,进行数据过滤,并返回* 其中T是混入了Condition[T]的子类*/def doFilter[T <: Condition[T]](data: List[T])(implicit condition: T => Boolean): List[T] = data.filter(_.judge(implicitly[T => Boolean])) //XXX 注意这里的改动
}/*** 定义一个Condition特质,包含condition方法,混入此特质的类,可以使用condition进行筛选** @tparam T 协变*/
trait Condition[+T] {def judge(condition: T => Boolean): Boolean
}/*** 诗人类*/
case class Poet(val name: String, val age: Int) extends Condition[Poet] {//复写方法,调用con函数,传入thisoverride def judge(condition: Poet => Boolean): Boolean = condition(this)
}
在这个改动版本中,我们其他地方都没变,只有class ContextBounds类的 doFilter方法进行类变化:
def doFilter[T <: Condition[T]](data: List[T])(implicit condition: T => Boolean): List[T] =data.filter(_.judge(implicitly[T => Boolean])) //XXX 注意这里的改动
- 可以看到,调用data.filter()时,我们并没有像刚才那样,直接将的condition传入,而是用了implicitly[T => Boolean]
- 这里的效果其实跟我们之前用隐式参数condition传入一样。因为scala标准库定义的implicitly这个方法,内容如下:
def implicitly[T](implicit t: T) = t
- 也就是说,implicitly的作用是获取作用域内已经生效的隐式转换,获取的方式,是通过传入隐式转换的类型来确定(这就是T类型参数的作用)。它会查找隐式转换类型为T的隐式转换定义,并把它的实例t返回。
- 所以我们这里调用的话,系统能获取到已经在作用域内生效的、类型为T => Boolean的隐式定义:condition,并放入方法内执行。也就是说,跟之前直接调用condition一致
- 这里的方法入参名:condition无论起什么都可以,可以叫
/*** 封装原方法的类,可以看到这里有一个类型参数U,并且我们的方法带上类型参数,可用度更加广了*/ abstract class MyFunctionClass[U] {val myFunction: (U => Boolean) }/** 上下文界定* author: leisu* date 2018/8/15 17:36*/ object ContextBounds extends App {/*XXX 改动一: 这里封装了下原先的方法,把它放到了MyFunctionClass类之下* 因为上下文引入必须是带有一个类型参数的类的实例* 虽然原先的方法时Function1[Poet, Boolean]的实例,但无法再给它加其他类型参数,所以封装*/implicit val ageCondition: MyFunctionClass[Poet] = new MyFunctionClass[Poet] {val myFunction: (Poet => Boolean) = _.age > 18};val cb = new ContextBoundsval list: List[Poet] = List(new Poet("李白", 35),new Poet("白居易", 19),new Poet("苏轼", 34),new Poet("辛弃疾", 17),new Poet("杜甫", 21),new Poet("张良", 18))println("完全上下界实现:" + cb.doFilter(list)) }class ContextBounds {/*** 根据传入的data和判断条件con,进行数据过滤,并返回* 这里类型参数变为: [T: MyFunctionClass] 有两个意思:* 1. 引入类一个类型参数T,并且这个类型参数与 MyFunctionClass无直接关联(不同于上界下界,需要是后面的父类或者子类)* 2. 自动为此方法添加了一个MyFunctionClass[T]的隐式入参,也就是上面object中定义的:implicit val ageCondition*/def doFilter[T: MyFunctionClass](data: List[T]): List[T] = data.filter(implicitly[MyFunctionClass[T]].myFunction(_)) //XXX 注意这里的改动 }/*** 定义一个Condition特质,包含condition方法,混入此特质的类,可以使用condition进行筛选** @tparam T 协变*/ trait Condition[T] {def judge(condition: T => Boolean): Boolean }/*** 诗人类*/ case class Poet(val name: String, val age: Int) extends Condition[Poet] {//复写方法,调用con函数,传入thisoverride def judge(condition: Poet => Boolean): Boolean = condition(this) }这里我们有几个改动:
- 定义了一个新类: MyFunctionClass[T],其中含有一个类型参数T。这个类是为了封装我们之前定义的(Poet => Boolean)方法。之所以定义这个类,是与上下文界定的语法有关的: 上下文界定语法要求传入有一个类型参数的类的实例。虽然说(Poet => Boolean)也是Function[Poet, Boolean]的实例,但是它没法再有一个类型参数,所以我这里采用了封装的思想,专门定义了MyFunctionClass类,用来传参。
- 在object ContextBounds中,我声明了这个类的实现:implicit val ageCondition: MyFunctionClass[Poet],并且定义它为隐式转换。这样,这个ageCondition就可以加载到其他需要此类型的隐式参数中了。
- 对于原先传入隐式参数的doFilter方法,我们现在做如下定义:
def doFilter[T: MyFunctionClass](data: List[T]): List[T] = data.filter(implicitly[MyFunctionClass[T]].myFunction(_))
这里可以看到并没有像之前那样,再方法体上直接传入隐式入参(implicit condition ...), 那是如何生效的呢?注意看之前的类型参数那里,这里的定义方法为[T: MyFunctionClass],这一步声明有两个效果:
- 引入了一个类型参数T,并且这个类型参数与 MyFunctionClass无直接父子类关系(不同于上界下界,需要是后面的父类或者子类)
- 自动为doFilter方法添加了一个MyFunctionClass[T]的隐式入参,也就是说,它会找到作用域内有效的、类型为MyFunctionClass[T]的隐式定义(即object ContextBounds中的:implicit val ageCondition),作为参数传入doFilter方法,并在方法体内,通过取此类型implicitly[MyFunctionClass[T]]再得到ageCondition: MyFunctionClass[Poet],调用myFunction真正取到(Poet => Boolean)的方法体,放入List.filter中做入参,依次调用。
这样整个通过上下文界定实现传入隐式参数的功能就全部实现。最后执行的效果为:
完全上下界实现:List(Poet(李白,35), Poet(白居易,19), Poet(苏轼,34), Poet(杜甫,21))可以看到与之前的结果相同
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
