implicit关键字详解
implicit
- implicit是scala中的一个关键字,下面从五个方面介绍下implicit
- 隐式转换函数:implicit def int2str(x:Int):String = x.toString
- 隐式类:implicit class Student(x: Int) { }
- 隐式参数:def Student[T](x:T,y:T)(implicit ordered: Ordering[T]):Int = ordered.read(x,y)
- 隐式值:implicit val x: Int = 0
- 隐式对象:implicit object obj {}
- Scala支持两种形式的隐式:
- 隐式值:用于给方法提供参数(隐式转换函数,隐式对象都可以作为隐式值为方法提供隐式参数)
- 隐式视图:用于对象类型间的转换使函数的调用能够编译通过(隐式转换函数,隐式类都可以看成是隐式视图的隐式转换)
- 作用:通过隐式转换可以极大的减少代码量,让编译器去去搜索作用域内的implicit修饰信息进行隐式转换、隐式赋值
- implicit修饰的都必须满足无歧义规则
- 隐式转换的存放的地点:需要导入包 :import com.bigdata.study.scala._
- 当前程序可见作用域
- 原类型的伴生对象中
- 其他object中
隐式转换函数
- 隐式转换函数:使用implicit关键字修饰的函数
- 隐式转换的前提:
- 隐式转换函数只接受一个参数,对于接受多参数的隐式函数来说就没有隐式转换的功能
implicit def int2str(x:Int):String = x.toString // 正确 implicit def int2str(x:Int,y:Int):String = x.toString // 错误
- 不支持嵌套的隐式转换
class A{ def hi = println("hi") } implicit def int2str(x:Int):String = x.toString implicit def str2A(x:Int,y:Int):A = new A "str".hi // 正确 1.hi // 错误
- 不能存在二义性,即同一个作用域不能定义两个相同类型的隐式转换函数(即是不能存在两个都是将类型a转换成类型b的函数)
implicit def int2str(x:Int):String = x.toString implicit def anotherInt2str(x:Int):A = x.toString
- 代码能够在不使用隐式转换的前提下能编译通过,就不会进行隐式转换(即当表达式在编译不通过的情况下才会调用隐式转换,如果编译通过就不调用)
- 隐式转换函数只接受一个参数,对于接受多参数的隐式函数来说就没有隐式转换的功能
- 一个对象调用一个函数编译不通过只有两个原因:a.fun(b)
- 当方法中的参数的类型b与目标类型不一致时
- 当对象a调用类中不存在的方法或成员时
- 隐式转换的顺序:隐式转换使表达式通过编译的顺序是 b到a.(先对b进行转换看是否通过,如果不通过在对a进行转换,如果都转换之后还不通过就报错)
- 隐式转换的作用:
- 编译器会在当前作用域寻找合适的隐式转换使将a或者对象b进行转换类型使编译通过
- 就是扩展已有的类,在不修改原有类的基础上为其添加新的方法、成员(把一种类型自动转换成另外一种类型,进而使用另外一种类型中的属性和方法)
例子:
object ImplicitDemo { // TODO:定义隐式转换函数,地点一 -> 当前程序可见作用域 implicit def man2SuperMan(man: Man): SuperMan = new SuperMan(man.name) def main(args: Array[String]): Unit = { // 平时的时候,普通人, 该上学的上学 val man = new Man("super") // 怪兽出现,毁灭人类,普通人变身奥特曼,吊打怪兽 import com.erongda.bigdata.scala.oop.demo07.AA._ man.emitLaser() } }
隐式类
- Scala2.10引入了一种叫做隐式类的新特性。隐式类指的是用implicit关键字修饰的类。在对应的作用域内,带有这个关键字的类的主构造函数可用于隐式转换。
- 限制条件:
- 只能在别的trait/类/对象内部定义。
object Test {implicit class person(x: Int)} // 正确! implicit class person(x: Double) // 错误!
- 构造函数只能携带一个非隐式参数(虽然我们可以创建带有多个非隐式参数的隐式类,但这些类无法用于隐式转换。)
implicit class Test(str: String) // 正确! implicit class Test[T](str: String, index: Int) // 错误! implicit class Test[T](str: String)(implicit index: Index) // 正确!
- 在同一作用域内,不能有任何方法、成员或对象与隐式类同名。(注意:这意味着隐式类不能是case class。)
object Test{ def main(args: Array[String]): Unit = { implicit class Test(x: Int) // 错误! val x = 1 implicit class x(y: Int) // 错误! implicit case class Baz(x: Int) // 错误! } }
- 只能在别的trait/类/对象内部定义。
- 隐式类和隐式转换函数区别:都是实现隐式转换,各有优点(比如:a想添加b方法)
- 隐式转换函数:首先创建一个有b方法的类c ,在写一个隐式转换函数fun(a)将对象a变成对象c
- 隐式类:构造一个隐式类c(a),将参数a的类型转换成自己的类型c,直接调用b方法
- 优缺点:隐式类比隐式转换函数更加简单,但是隐式转换函数的功能更加强大,因为需要多个对象object1,object2,object3添加方法,隐式类需要为每一个对象创建一个隐式类(方法),隐式转换函数只需要创建一个对象(方法),为每个object1,object2,object3添加一个隐式转换函数即可
object ImplicitTest { class Man{} //隐式类 implicit class Teacher(man: Man){ def fly: Unit = { println("在教书") } } //隐式转换函数 class Student(man:Man){ def study: Unit ={ println("在学习") } } implicit def ManToStudent(man:Man)=new Student(man) def main(args: Array[String]): Unit = { val man = new Man man.fly man.study } }
隐式参数
- 隐式参数:使用关键字implicit进行标识参数就是隐式参数,隐式参数一般和普通的参数放在两个不同的括号中(def student(name:String)(implicit age:Int))
- 函数的调用:在调用含有隐式参数的函数时可以不用传递隐式参数,编译器会自动寻找合适的隐式值当做隐式参数,而只有用implict标记过的值、对象、函数才能被找到。
- 隐式参数有默认值,可以用赋值,也可以当做一个普通的参数那样进行赋值
寻找隐式值
object ImplicitTest { def main(args: Array[String]): Unit = { implicit val ages = 12 def student(name:String)(implicit age:Int): Unit ={ println(s"$name 今年 $age 岁了") } student("Tom") student("Tom")(15) } }
寻找隐式对象和隐式函数
object ImplicitTest { def main(args: Array[String]): Unit = { // 例如自动寻找隐式对象: implicit object Student { def Study(s:String) = println(s"好好学习 $s!") } def test(s:String)(implicit stu: Student.type ) = { stu.Study(s) } test("Tom") // // 自动寻找隐式函数: implicit def int2str(x: Int): String = x.toString def test1(x: Int, fun: String => Unit) (implicit age: Int => String) = { fun(age(x)) } test1(12, println) // 打印出"12" } }