即日起在codingBlog上分享您的技术经验即可获得积分,积分可兑换现金哦。

Scala中隐式转换深度整理

编程语言 zhanghytc 7℃ 0评论

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需要传进去一个隐式值,


记住:


看到类中有 <% 就想到在某个地方导入了一个隐式转换函数


看到 : 就想到类中某个地方导入了一个隐式值

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

转载请注明:CodingBlog » Scala中隐式转换深度整理

喜欢 (0)or分享 (0)
发表我的评论
取消评论

*

表情