Scala 语言学习五——高级语法

隐式(implicit) 详解

通过隐式转换,程序员可以在编写Scala程序时故意漏掉一些信息,让编译器去尝试在编译期间自动推导出这些信息来,这种特性可以极大的减少代码量,忽略那些冗长,过于细节的代码。

掌握 implicit 的用法是阅读 spark 源码的基础, 也是学习 Scala 其它的开源框架的关键,
implicit 可分为:

  1. 隐式参数
  2. 隐式转换类型
  3. 隐式类

隐式参数

定义方法时, 可以把参数列表标记为 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)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值