文章目录

  • 概述
  • 分类
  • 隐式转换
  • 隐式参数
  • 补充案例:通过隐式转换,获取列表中所有元素的平均值


概述

隐式转换和隐式参数是Scala中非常有特色的功能,也是Java等其他编程语言没有的功能。我们可以很方便地利用
隐式转换来丰富现有类的功能
。在后续编写Akka并发编程, Spark, Flink程序时都会经常用到它们。

分类

隐式转换

概述
指用implicit关键字声明的带有单个参数的方法,该方法是自动被调用的,用来实现自动将某种类型的数据转换为另外一种类型的数据

注:implicit关键字是在Scala的2.10版本出现的

使用步骤

  1. object单例对象中定义隐式转换方法

隐式转换方法解释:就是用implicit关键字修饰的方法

  1. 在需要用到隐式转换的地方,引入隐式转换

类似于导包,通过import关键字实现

  1. 当需要用到隐式转换方法时,程序会自动调用

案例

  • 需求:
    通过隐式转换, 让File类的对象具备有read功能(即: 实现将文本中的内容以字符串形式读取出来).
  • 步骤:
  1. 创建RichFile类,提供一个read方法,用于将文件内容读取为字符串
  2. 定义一个隐式转换方法,将File隐式转换为RichFile对象
  3. 创建一个File类的对象,导入隐式转换,调用File的read方法.
  • 代码:
object 隐式转换 {
  import java.io.File
  import scala.io.Source
  //案例: 演示 隐式转换, 手动导入.
  // /*隐式转换: 概述: 用implicit修饰的 带有单个参数的方法, 该方法会被自动调用.
  // 前提: 需要手动引入.
  // 作用: 用来丰富某些对象的功能的. 大白话解释: 某个对象没有某个功能, 通过特定手段让他具有此功能.
  // 简单理解: 这个类似于Java中的装饰设计模式.
  // BufferedReader br = new BufferedReader(new FileReader("a.txt)) //这样写会报错, 必须传入一个 要被升级功能的 对象.
  // BufferedReader br = new BufferedReader("a.txt") */

  //1. 创建RichFile类,提供一个read方法,用于将文件内容读取为字符串
  class RichFile(file:File){
    //定义一个read()方法, 用来读取数据(获取一行行的数据).
   def read()= Source.fromFile(file:File).getLines()
  }
  //2. 定义一个单例对象, 包含一个方法, 该方法用于将: 普通的File对象 转换成 RichFile对象.
  object ImplicitDemo {
  // 定义一个方法, 该方法用于将: 普通的File对象 转换成 RichFile对象.
  implicit def file2RichFile(file:File) = new RichFile(file)
  //注:
  //自动导入隐式转换方法:也可将隐式转换方法,直接定义在main方法中
  }
  def main(args: Array[String]): Unit = {
    //3. 非常非常非常重要的地方: 手动导入 隐式转换.
    import ImplicitDemo.file2RichFile
    //4. 创建普通的File对象, 尝试调用其read()功能.
    val str = new File("user.txt").read()
    println(str.toList)
    /*执行流程: 
    1. 先找File类有没有read(), 有就用. 
    2. 没有就去, 查看有没有该类型的隐式转换, 将该对象转成其他对象. 
    3. 如果没有隐式转换, 直接报错. 
    4. 如果可以将该对象升级为其他对象, 则查看升级后的对象中有没有指定方法, 有, 不报错, 没有就报 
    错. 如下的案例执行流程: 
    1. file对象中没有read()方法. 
    2. 检测到有 隐式转换将 file对象 转成 RichFile对象. 
    3. 调用RichFile对象的read()方法, 打印结果. */
  }
}

隐式转换

  1. 当对象调用类中不存在的方法或成员时,编译器会自动对该对象进行隐式转换
  2. 当方法中的参数类型与目标类型不一致时,编译器也会自动调用隐式转换

隐式参数

概述
在Scala的方法中, 可以带有一个 标记为implicit的参数列表 。调用该方法时, 此参数列表可以不用给初始化值, 因 为 编译器会自动查找缺省值,提供给该方法 。

使用步骤

  1. 在方法后面添加一个参数列表,参数使用implicit修饰
  2. 在object中定义implicit修饰的隐式值
  3. 调用方法,可以不传入implicit修饰的参数列表,编译器会自动查找缺省值
    注:
  1. 和隐式转换一样,可以使用implicit修饰
  2. 如果在当前作用域定义了隐式值,就会自动进行导入

案例
需求:

  • 定义一个show方法,实现将传入的值,使用指定的前缀分隔符和后缀分隔符包裹起来

例如:show(“张三”)("<<<", “>>>”), 则运行结果为: <<<张三>>>

  • 使用隐式参数定义分隔符
  • 调用该方法,并打印结果

代码:

object 隐式参数 {
  //需求: 定义一个方法, 传入一个姓名, 然后用指定的前缀和后缀将该名字包裹.
  // 1. 定义一个方法show(), 接收一个姓名, 在接受一个前缀, 后缀信息(这个是隐式参数).
  def show(name:String)(implicit delimit:(String, String)) = delimit._1 + name + delimit._2

  //2. 定义一个单例对象, 给隐式参数设置默认值.
  object ImplicitParam {
    implicit val delimit_defalut = "<<<" -> ">>>"
    //注:也可将将该方法定义到main方法中,实现自动导入
  }
  def main(args: Array[String]): Unit = {
    //3. 手动导入: 隐式参数.
    import ImplicitParam.delimit_defalut
    //4. 尝试调用show()方法.
    println(show("张三"))
    println(show("张三")("(((" -> ")))"))
  }
}

补充案例:通过隐式转换,获取列表中所有元素的平均值

需求

  1. 定义一个RichList类, 用来给普通的List添加avg()方法,用于获取列表元素的平均值.
  2. 定义avg()方法, 用来获取List列表中所有元素的平均值.
  3. 定义隐式转换方法, 用来将普通List对象转换为RichList对象.
  4. 定义List列表, 获取其中所有元素的平均值.

代码

object 获取列表元素平均值 {
  //1. 定义一个RichList类, 用来给普通的List添加avg()方法.
  class RichList(list:List[Int]) {
    //2. 定义avg()方法, 用来获取List列表中所有元素的平均值.
    def avg() = {
      if(list.size == 0) None else Some(list.sum / list.size)
    }
  }
  //main方法, 作为程序的主入口.
  def main(args: Array[String]): Unit = {
    //3. 定义隐式转换方法.
    implicit def list2RichList(list:List[Int]) = new RichList(list)
    //4. 定义List列表, 获取其中所有元素的平均值.
    val list1 = List(1, 2, 5, 4, 3)
    println(list1.avg()) 
  }
}