Scala中隐式转换整理

来源:互联网 发布:淘宝知名店铺 编辑:程序博客网 时间:2024/06/08 04:50

Scala中隐式转换
在Scala中,如果对某个对象功能进行增强,,就需要使用到隐式转换
下面以对象排序这个例子来讲解一下Scala中的隐式转换
我们知道,在java中或者scala中,我们可以对数值型进行比较,但是如果对两个对象进行比较呢?

在java中,要实现两个对象比较,就必须实现comparable接口,实现compareTo接口,如下:

class Boy(val name:String,val faceValue:Int) extends Comparable[Boy]{
override def compareTo(o: Boy) = {
this.faceValue - o.faceValue
}
}

object Boy{
def main(args: Array[String]) {
val b1 = new Boy(“a1”,90)
val b2 = new Boy(“b1”,91)
val arr = Array(b1,b2)
// import MyPredf.OrderingBoy
val sorted = arr.sortByv[Boy] (t=>t).reverse
println(sorted(0).name + “,faceValue:” + sorted(0).faceValue)
}
}

但是在Scala中,如果我希望能够直接比较,比如 b1 < b2 , b1 gt b2 这样,是不是就要对boy这个对象进行增强了, 像<,gt,lt, >等 这些在Ordering对象中, 说明需要把boy对象增强成Ordering[Boy],这个过程就必须使用隐式转换了.
在Scala中用于比较的有两个对象,Ordering,Ordered,其中Ordered比Ordering更加高级.
在Scala中要对目标类进行增强,比如调用file的read()方法将文件中数据全部读出来,但是File本身没有这个方法,这时候就需要增强
object MainApp {
def main(args: Array[String]) {
import RichFile._
println(new File(“C:\Users\root\Desktop\word.txt”).read)
}
}

class RichFile(val from:File) {
def read() :String ={
Source.fromFile(from.getPath,”ISO-8859-1”).mkString
}
}

object RichFile{
implicit def file2RichFile(from:File) = new RichFile(from)
}
这样就隐式的增强File类的方法,隐式转换会让你的程序非常的灵活,比如这个类是你写的,我想增强这个类,我不需要修改原有代码,只需要在上面增强一下就可以了

隐式转换

/**
* Created by root on 2016/11/30.
*
*/
class Choose [T]{
def choose(first:T,second:T) ={
if(first > second) first else second
}
}
这时候我想通过隐式转换增强一下这个T,这就意味着要传一些隐式参数,这时候有2种方式,一种是传隐式函数,一种是传隐式值
class Choose [T]{
def choose(first:T,second:T) (implicit ord:T => Ordered[T]) ={
if(first > second) first else second
}
}
将T类型变成Oedered类型 这时候就可以了,但是具体比较什么呢 ?比较规则没定义,这时候需要自己定义一个object, 其中用implicit 修饰的函数或者方法,(方法会通过_转换成函数),这里定义成方法
object MyPredf {

implicit def grilToOrdered(girl: Girl) = new Ordered[Girl] {
override def compare(that: Girl): Int = {
if (girl.faceValue == that.faceValue) {
that.age - girl.age
} else {
girl.faceValue - that.faceValue
}
}
}
}

其中Ordered是一个trait,new一个Ordered[Girl],则new一个实现
object Choose{
def main(args: Array[String]) {
import MyPredf._
val g1 = new Girl(“a1”,88,20)
val g2 = new Girl(“a2”,90,18)
val ch: Choose[Girl] = new Choose[Girl]
val rs: Girl = ch.choose(g1,g2)

println("name: " + rs.name + ",faceValue:" + rs.faceValue + ",age:" + rs.age)

}
}
在调用ch.choose(g1,g2)时候,有一个隐式转换,会到Ordered的上下文中去找跟它类型一样的,(备注: 上下文是指一个object 中由implicit修饰的方法或者函数)

这种运行是正常的,但是有的时候,你会发现没有必要这么写,这个隐式函数可以不要它,这时候可以用viewBound来实现,如下:
class Choose [T <% Ordered[T]]{
def choose(first:T,second:T) ={
if(first > second) first else second
}
}

在定义上下文的时候要明确传进去的是什么,返回的是什么?
传进去的是目标对象,返回的是增强之后的对象

结论一:ViewBound相当于传进去一个隐式函数,这个函数会到上下文中去找

第二种方式:
上面一种方式是传隐式函数,下面传隐式参数
class Choose [T]{
def choose(first:T,second:T) (implicit ord:T => Ordered[T]) ={
if(first > second) first else second
}

**def select(first:T,second:T)(implicit ord:Ordering[T]): T ={
if(ord.gt(first,second)) first else second
}**
}

这时候还需要定义上下文,找一个Ordering[T]这种类型的
这时候在MyPredef中加上比较规则

implicit object OrderingGirl extends Ordering[Girl]{
override def compare(x: Girl, y: Girl): Int = {
if(x.faceValue == y.faceValue){
y.age - x.age
} else{
x.faceValue - y.faceValue
}
}
}

有时候你会发现,Scala也不这么写,这么写还是有点丑,Scala这么写:

class Choose [T:Ordering[T]]{
// def choose(first:T,second:T) (implicit ord:T => Ordered[T]) ={
// if(first > second) first else second
// }

def select(first:T,second:T): T ={
//这时候就要定义ord
val ord = implicitly[Ordering[T]]
if(ord.gt(first,second)) first else second
}

}

object Choose{
def main(args: Array[String]) {
import MyPredf._
val g1 = new Girl(“a1”,88,20)
val g2 = new Girl(“a2”,90,18)
val ch: Choose[Girl] = new Choose[Girl]
// val rs: Girl = ch.choose(g1,g2)
val rs1: Girl = ch.select(g1,g2)
println(“name: ” + rs1.name + “,faceValue:” + rs1.faceValue + “,age:” + rs1.age)

}
}

还有一种方式更爽,在select在方法中,我还是想使用<,>这样的运算符,也就是想使用Ordered[T],这时候可以将Ordering转换成Ordered[T]
如下写法:

def select(first:T,second:T): T ={
//这时候就要使用ord
// val ord = implicitly[Ordering[T]]
import Ordered.orderingToOrdered
if(first > second) first else second
}
卧槽, 是不是屌爆了,定义的Class中类型没有指明是ViewBound还是ContextBound,这说明必须通过柯里化的方式传一个隐式函数或参数
柯里化是隐式转换的基础

结论二: ContextBound需要传进去一个隐式值,
记住:
看到类中有 <% 就想到在某个地方导入了一个隐式转换函数
看到 : 就想到类中某个地方导入了一个隐式值

另外: 逆变通常作为方法的输入,邪变通常作为方法的返回

0 0
原创粉丝点击