隐式(implicit) 详解
通过隐式转换,程序员可以在编写Scala程序时故意漏掉一些信息,让编译器去尝试在编译期间自动推导出这些信息来,这种特性可以极大的减少代码量,忽略那些冗长,过于细节的代码。
掌握 implicit 的用法是阅读 spark 源码的基础, 也是学习 Scala 其它的开源框架的关键,
implicit 可分为:
- 隐式参数
- 隐式转换类型
- 隐式类
隐式参数
定义方法时, 可以把参数列表标记为 implicit, 表示该参数是隐式参数。 一个方法只会有一
个隐式参数列表, 置于方法的最后一个参数列表。
def say(implicit content: String = "明天10.1拉") = println(content)
say //则会打印明天10.1拉
def say(implicit content: String) = println(content)
implicit msg="牛逼"
say //则会打印牛逼
如果方法有多个隐式参数, 只需一个implicit 修饰即可。
并且常常配合柯里化一起使用
def fire(x: Int)(implicit a:String, b: Int = 9527)
//调用时候只需
fire(2)
当调用包含隐式参数的方法是, 如果当前上下文中有合适的隐式值, 则编译器会自动为该
组参数填充合适的值, 且上下文中只能有一个符合预期的隐式值。 如果没有编译器会抛出
异常。 当然, 标记为隐式参数的我们也可以手动为该参数添加默认值。
隐式转换类型
定义一个隐式的方法,将浮点型转化为int类型
implicit def double2Int(double: Double) = {
println("---double2Int---")
double.toInt
}
implicit val fdouble2Int = (double: Double) => {
println("---fdouble2Int---")
double.toInt
}
// age是一个Int类型,但是赋值的时候却是一个浮点型,此刻编译器会在当前上下文中找一个隐式转换,找一个能把浮点型变成Int的隐式转换
val age: Int = 20.5
println(age)
}
首先先定义一个RichFile类,类中含方法使得可以通过文件查看行数和内容。再通过隐式转化,将file转化为RichFile
//首先先定义一个类,类中含方法使得可以通过文件查看行数和内容
class RichFile(file: File) {
/**
* 返回文件的记录行数
* @return
*/
def count(): Int = {
val fileReader = new FileReader(file)
val bufferedReader = new BufferedReader(fileReader)
var sum = 0
try {
var line = bufferedReader.readLine()
while (line != null) {
sum += 1
line = bufferedReader.readLine()
}
} catch {
case _: Exception => sum
} finally {
fileReader.close()
bufferedReader.close()
}
sum
}
//定义一个隐式方法,将file转化为Richfile
implicit def fileToRichFile(file:File)=new RichFile(file)
def main(args: Array[String]): Unit = {
val file = new File("D:\\log\\2017-09-21\\access.log")
println("Count = "+file.count()) //现在就可以简单的直接调用count方法了
}
隐式类
// 隐式类 - 只能在静态对象中使用
//可以将file类转化为FileRead
implicit class FileRead(file: File) {
def read = Source.fromFile(file).mkString
}
val file = new File("D:\\log\\2017-09-21\\access.log")
println(s"FileContent = ${file.read}")
}
泛型
通俗的讲, 比如需要定义一个函数, 函数的参数可以接受任意类型。 我们不可能一一列举所
有的参数类型重载函数。
那么程序引入了一个称之为泛型的东西, 这个类型可以代表任意的数据类型。
例如 List, 在创建 List 时, 可以传入整形、 字符串、 浮点数等等任意类型。 那是因为 List 在
类定义时引用了泛型。
在 Scala 定义泛型用[T], s 为泛型的引用
abstract class Message[T](s: T) {
def get: T = s
}
子类扩展的时候, 约定了具体的类型
class StrMessage[String](msg: String) extends Message(msg)
class IntMessage[Int](msg: Int) extends Message(msg)
子类的使用
val s = new StrMessage("i hate you !")
val i = new IntMessage(100)
枚举类型解释
// Scala 枚举类型,则该Em类型只有四个选择上衣, 内衣, 裤子, 袜子
object Em extends Enumeration {
type Em = Value
val 上衣, 内衣, 裤子, 袜子 = Value
}
// 定义一个泛型类
class Clothes[A,B,C](val clothesType: A, var color: B, var size: C)
object Test {def main(args: Array[String]): Unit = {
// 创建一个对象, 传入的参数为 Em, String, Int,可自动识别
val c1 = new Clothes(Em.内衣, "Red", 36)
c1.size = 38
println(c1.clothesType, c1.size)
// new 的时候, 指定类型。 那么传入的参数, 必须是指定的类型
val c2 = new Clothes[Em, String, String](Em.上衣, "黑色", "32B")
println(c2.size)
}
}
}
// 定义一个函数, 可以获取各类 List 的中间位置的值
val list1 = List("a","b","c")
val list2 = List(1,2,3,4,5,6)
// 定义一个方法接收任意类型的 List 集合
def getData[T](l: List[T])={
l(l.length/2)
类型约束
上界(Upper Bounds)
Upper Bounds
在 Java 泛型里表示某个类型是 Test 类型的子类型, 使用 extends 关键字:
<T extends Test>
或用通配符的形式:
<? extends Test>
这种形式也叫 upper bounds(上限或上界), 同样的意思在 Scala 的写法为:
[T <: Test]
//或用通配符:
[_ <: Test]
def pr(list : List[_ <: Any]) {
list.foreach(print)
}
下界(lower bounds)
在 Java 泛型里表示某个类型是 Test 类型的父类型, 使用 super 关键字:
<T super Test>
//或用通配符的形式:
<? super Test>
这种形式也叫 lower bounds(下限或下界), 同样的意思在 scala 的写法为:
[T >: Test]
//或用通配符:
[_ >: Test]
上界的举例
类似java中的 <T extends Comparable
不会发生隐式转换,除非用户显示的指定
T 实现了 Comparable 接口
/**
* <: 上界 upper bounds
* 类似java中的 <T extends Comparable>
* 不会发生隐式转换,除非用户显示的指定
* T 实现了 Comparable 接口
* T 是Comparable的子类
*/
class CmpComm[T <: Comparable[T]](o1: T, o2: T) {
def bigger = if(o1.compareTo(o2) > 0) o1 else o2
}
val cmpcom = new CmpComm(1, 2) // 上界的时候会报错,因为int类不是Comparable的子类
val cmpcom = new CmpComm(Integer.valueOf(1), Integer.valueOf(2)) //方法一:主动声明才会隐式转化
val cmpcom = new CmpComm[Integer](1, 2) //方法二:主动声明才会隐式转化
视图界定
- <% 视图界定 view bounds
- 会发生隐式转换
第一个版本
( Comparable类有compareTo方法,无>这个方法)
class CmpComm[T <% Comparable[T]](o1: T, o2: T) {
def bigger = if(o1.compareTo(o2) > 0) o1 else o2
}
第二个版本
(Ordered中有>这个方法)
class CmpComm[T <% Ordered[T]](o1: T, o2: T) {
def bigger = if(o1 > o2) o1 else o2
}
不需要主动声明,会自动发送隐式转化
val cmpcom = new CmpComm(1, 2)
比较类型
方式一:利用上界界定,并且令类继承Comparable接口
class CmpComm[T <: Comparable[T]](o1: T, o2: T) { //上界界定可以实现
def bigger = if(o1.compareTo(o2) > 0) o1 else o2
}
class CmpComm[T <% Ordered[T]](o1: T, o2: T) { //视图界定可以实现
def bigger = if(o1>o2) o1 else o2
}
class Students(val name: String, val age: Int) extends Ordered[Students]{
override def compare(that: Students): Int = this.age - that.age
override def toString: String = this.name + "\t" + this.age
val tom = new Students("Tom", 18)
val jim = new Students("Jim", 20)
val cmpcom = new CmpComm(tom, jim)
println(cmpcom.bigger)
}
方式二:利用视图界定,并且令类继承Ordered接口
class CmpComm[T <% Ordered[T]](o1: T, o2: T) { //视图界定可以实现
def bigger = if(o1>o2) o1 else o2
}
class Students(val name: String, val age: Int) extends Ordered[Students]{
override def compare(that: Students): Int = this.age - that.age
override def toString: String = this.name + "\t" + this.age}
val tom = new Students("Tom", 18)
val jim = new Students("Jim", 20)
val cmpcom = new CmpComm(tom, jim)
println(cmpcom.bigger)
方式三:利用视图界定,并且利用隐式转化将类转化为Ordered类,利用视图界定会发生隐式转化的特性,这种最省代码
class CmpComm[T <% Ordered[T]](o1: T, o2: T) { //视图界定可以实现
def bigger = if(o1>o2) o1 else o2
//定义一个隐式方法,隐式将Student-》Ordered[Student]
implicit def studentToOrderedStu(stu : Students)=new Ordered[Students]{
override def compare(that:Students):Int=stu.age-that.age}
class Students(val name: String, val age: Int) { //该类不需要继承Ordered
override def toString: String = this.name + "\t" + this.age
}
val tom = new Students("Tom", 18)
val jim = new Students("Jim", 20)
val cmpcom = new CmpComm(tom, jim)
println(cmpcom.bigger)
}
上下文界定
写法一:
//隐式地传入一个比较器
class CmpComm[T: Ordering](o1: T, o2: T)(implicit cmptor: Ordering[T]) {
def bigger = if (cmptor.compare(o1, o2) > 0) o1 else o2
}
//设定一个隐式方法,将学生类隐式地转化为一个比较器
implicit val comparatorStu = new Ordering[Students] {
override def compare(x: Students, y: Students): Int = x.age - y.age
}
val tom = new Students("Tom", 18)
val jim = new Students("Jim", 20)
val cmpcom = new CmpComm(tom, jim)
println(cmpcom.bigger)
写法二
//隐式地传入一个比较器
class CmpComm[T: Ordering](o1: T, o2: T) {
def bigger = {
def inner(implicit cmptor: Ordering[T]) = cmptor.compare(o1, o2)
if (inner > 0) o1 else o2
}
}
//设定一个隐式方法,将学生类隐式地转化为一个比较器
implicit val comparatorStu = new Ordering[Students] {
override def compare(x: Students, y: Students): Int = x.age - y.age
}
val tom = new Students("Tom", 18)
val jim = new Students("Jim", 20)
val cmpcom = new CmpComm(tom, jim)
println(cmpcom.bigger)
写法三:
//隐式地传入一个比较器
class CmpComm[T: Ordering](o1: T, o2: T) {
def bigger = {
val cmptor = implicitly[Ordering[T]]
if(cmptor.compare(o1, o2) > 0) o1 else o2
}
}
//设定一个隐式方法,将学生类隐式地转化为一个比较器
implicit val comparatorStu = new Ordering[Students] {
override def compare(x: Students, y: Students): Int = x.age - y.age
}
val tom = new Students("Tom", 18)
val jim = new Students("Jim", 20)
val cmpcom = new CmpComm(tom, jim)
println(cmpcom.bigger)