第 9 章 隐式转换和隐式值

一、隐式转换

1、隐式函数基本介绍
	隐式转换函数是以implicit关键字声明的带有单个参数的函数。这种函数将会自动应用,将值从
一种类型转换为另一种类型。

隐式函数快速入门

	使用隐式函数可以优雅的解决数据类型转换。

示例代码:

package com.lj.scala.implicitly

/**
  * @author Administrator
  * @create 2020-03-12
  */
object ImplicitTest01 {

    def main(args: Array[String]): Unit = {

        // Double 是输入类型, Int 是转换后的类型
        // 隐士函数应当在作用域才能生效
        implicit def DoubleToInt(d: Double): Int = {
            d.toInt
        }

        val num: Int = 4.56
        println("num=" + num)   // num=4

    }

}

隐式转换的注意事项和细节

1. 隐式转换函数的函数名可以是任意的,隐式转换与函数名称无关,只与函数签名(函数参数类型
和返回值类型)有关;
2. 隐式函数可以有多个(即:隐式函数列表),但是需要保证在当前环境下,只有一个隐式函数能被识别

	//在当前环境中,不能存在满足条件的多个隐式函数
	implicit def a(d: Double) = d.toInt
	implicit def b(d: Double) = d.toInt 
	val i1: Int = 3.5 //(X)在转换时,识别出有两个方法可以被使用,就不确定调用哪一个,所以出错
	println(i1)

二、隐式转换丰富类库功能

基本介绍

	如果需要为一个类增加一个方法,可以通过隐式转换来实现(动态增加功能),比如想为MySQL
类增加一个delete方法;

分析解决方案

	在当前程序中,如果想要给MySQL类增加功能是非常简单的,但是在实际项目中,如果想要增
加新的功能就会需要改变源代码,这是很难接受的。而且违背了软件开发的OCP开发原则 (闭合原
则 open close priceple) ,在这种情况下,可以通过隐式转换函数给类动态添加功能。

示例代码:

package com.lj.scala.implicitly

/**
  * @author Administrator
  * @create 2020-03-12
  */
object ImplicitTest02 {

    def main(args: Array[String]): Unit = {

        val computer = new Computer
        computer.internet("Computer")

        /**
          * 如果此时想增加一个新的功能,而且不在原来的代码(类)中添加代码,此时
          * 可以使用隐式转换函数给指定的类添加功能。
          * 例如:给Computer类增加一个聊天的功能,此时可以应用隐士函数
          *
          * 注意事项:
          * 1. 隐式转换函数与函数参数类型和函数返回值类型有关,所以在使用的时候应当注意;
          * 2. 形参的类型:就是我们需要扩展类或者扩展数据类型功能的类型;
          * 3. 返回的类型:就是我们需要的该功能原本所对应的对象类型或者需要的数据类型。
          */

        implicit def addComputerChat(computer: Computer): Phone = {
            new Phone
        }

        // 此时Computer类就可以通过隐士函数调用chat()方法
        computer.chat("Computer") 

    }

}

class Computer {

    def internet(name: String): Unit = {
        println(name + "可以用来上网学习编程!")
    }

}


class Phone {

    def study(): Unit = {
        println("手机可以用来学习喜欢的知识!")
    }

    def chat(name: String): Unit = {
        println(name + "可以用来上网聊天!")
    }

}
=====================运行结果===============================
Computer可以用来上网学习编程!
Computer可以用来上网聊天!
=====================运行结果===============================

三、隐式值

基本介绍

	隐式值也叫隐式变量,将某个形参变量标记为implicit,所以编译器会在方法省略隐式参数的
情况下去搜索作用域内的隐式值作为缺省参数。

示例代码【包括隐式值、隐式参数默认值、传值之间的优选级】:

package com.lj.scala.implicitly

/**
  * @author Administrator
  * @create 2020-03-12
  */
object ImplicitTest03 {

    def main(args: Array[String]): Unit = {

        // 隐式值
        implicit val name: String = "Tom"

        /**
          * 此时函数sayHello参数指明为implicit(隐式参数),所以在调用该方法时
          * 如果不传入形参值,此时就会在作用域内查找隐士值作为参数传给函数sayHello
          * 如果传入形参值,此时只会用传入的值
          */
        // 使用隐士值不用带(), 否则会报错
        sayHello   // Hello Tom
        // 不使用隐式值
        sayHello("Jack")   // Hello Jack

        /**
          * 测试隐式值、默认值、传值 三者之间的优选级
          * 注意:当一个隐士参数匹配不到隐式值,其仍然会使用默认值。
          */
        // 1. 隐式值与隐式默认值同时存在的时候,“隐式值(implicit)”的优选级最高
        play   // Tom正在玩吃鸡游戏!
        // 2. 当函数后面加入()【此时相当于传入了隐式默认值】,但是没有传值,此时“隐式默认值”的优先级最高
        play()   // Leo正在玩吃鸡游戏!
        // 3. 隐式值、隐式默认值、传值三者同时存在是“传值”的优选级最高
        play("Rose")   // Tom正在玩吃鸡游戏!
        
        getInfo(25)   // 这位学生的年龄是25岁!
        // 4. 当没有隐式值,没有隐式参数又没有给默认值,而且也没有传值,此时就会会报错!
        // getInfo

    }


    def sayHello(implicit name: String): Unit = {
        println("Hello " + name)
    }

    def play(implicit name: String = "Leo"): Unit = {
        println(name + "正在玩吃鸡游戏!")
    }

    def getInfo(implicit age: Int): Unit = {
        println("这位学生的年龄是" + age + "岁!")
    }

}

四、隐式类

基本介绍

	在scala2.10后提供了隐式类,可以使用implicit声明类,隐式类的非常强大,同样可以扩展类
的功能,比前面使用隐式转换丰富类库功能更加的方便,在集合中隐式类会发挥重要的作用。

隐式类使用有如下几个特点

1. 其所带的构造参数有且只能有一个;
2. 隐式类必须被定义在“类”或“伴生对象”或“包对象”里,即隐式类不能是 顶级的(top-level  objects);
3. 隐式类不能是case class;
4. 作用域内不能有与之相同名称的标识符。

示例代码:

package com.lj.scala.implicitly

/**
  * @author Administrator
  * @create 2020-03-12
  */
object ImplicitTest04 {

    def main(args: Array[String]): Unit = {

        val cat = new Cat
        cat.eat("猫")   // 猫会抓老鼠!
        // Cat对象中没有对应的addName() 方法,所以此时会进行隐式转换
        cat.addName("咖啡")   // 这只花猫的名字叫:咖啡
        // Cat对象中虽然有addAge()方法,但是类型不匹配,所以会进行隐式转换
        cat.addAge(3)

        cat.age = 2
        // cat调用自己对象中的方法
        cat.addAge("Toney")

    }

    /**
      * 此时的AddFunction会对应生成隐式类,当我们在作用域内创建Cat对象的时候
      * 该隐式类就会生效,这个工作是编译器来完成的。
      *
      * 隐式转换的时机:
      * 1. 当方法中的参数的类型与目标类型不一致时,会在作用域内调用隐式转换;
      * 2. 当对象调用所在类中不存在的方法或成员时,编译器会自动将对象进行隐式转换(根据类型)。
      */

    implicit class AddFunction(val cat: Cat) {

        def addName(name: String): Unit = {
            println("这只花猫的名字叫:" + name)
        }

        def addAge(age: Int): Unit = {
            println("这只咖啡猫今年" + age + "岁了!")
        }

    }

}

class Dog {

    def run(name: String): Unit = {
        println(name + "跑的快!")
    }

}

class Cat {

    var age: Int = _

    def eat(name: String): Unit = {
        println(name + "会抓老鼠!")
    }

    def addAge(name: String): Unit = {
        println(name + "猫特别懒,今年" + age + "岁了!")
    }

}
=====================运行结果===============================
猫会抓老鼠!
这只花猫的名字叫:咖啡
这只咖啡猫今年3岁了!
Toney猫特别懒,今年2岁了!
=====================运行结果===============================
1、隐式解析机制

即编译器是如何查找到缺失信息的,解析具有以下两种规则

1. 首先会在当前代码作用域下查找隐式实体(隐式方法、隐式类、隐式对象),大多数都是这种
情况。
2. 如果第一条规则查找隐式实体失败,会继续在隐式参数的类型的作用域里查找。类型的作用域
是指与该类型相关联的全部伴生模块,一个隐式实体的类型“T”它的查找范围如下
(第二种情况范围广且复杂,在使用时应当尽量避免出现):
a)  如果“T”被定义为“T with A with B with C”,那么A,B,C都是“T”的部分,在“T”的隐式解析过程中,
它们的伴生对象都会被搜索。
b)  如果“T”是参数化类型,那么类型参数和与类型参数相关联的部分都算作“T”的部分,比如
“List[String]”的隐式搜索会搜“List”的伴生对象和“String”的伴生对象。
c)  如果“T”是一个单例类型p.T,即“T”是属于某个“P”对象内,那么这个“P”对象也会被搜索。
d)  如果“T”是个类型注入S#T,那么“S”和“T”都会被搜索。
2、隐式转换的前提

在进行隐式转换时,需要遵守两个基本的前提

不能存在二义性
隐式操作不能嵌套使用  // 如:隐式转换函数

示例代码:

package com.lj.scala.implicitly

/**
  * @author Administrator
  * @create 2020-03-12
  */
object ImplicitTest01 {

    def main(args: Array[String]): Unit = {

        // Double 是输入类型, Int 是转换后的类型
        // 隐士函数应当在作用域才能生效

        implicit def DoubleToInt(d: Double): Int = {
            d.toInt
            // val age: Int = 3.6   // 隐式的嵌套使用,本身也会报错
        }

        val num: Int = 4.56
        println("num=" + num)   // num=4

    }

}

对以前的知识回顾,加深基础知识!
学习来自:北京尚硅谷韩顺平老师—尚硅谷大数据技术之Scala
每天进步一点点,也许某一天你也会变得那么渺小!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值