【Scala】什么是隐式转换?它又能用来干嘛?该怎么用


定义

隐式参数

隐式参数指在函数或者方法中,定义一个用implicit修饰的参数,此时Scala会尝试找到一个指定类型的,用implicit修饰的参数,即隐式值,并注入参数。
Scala会在两个范围进行查找:
1.当前作用域内可见的val或var定义的隐式变量
2.隐式参数类型的伴生对象内的隐式值
 

隐式转换

当Scala编译器进行类型匹配时,如果找不到合适的候选,那么隐式转化提供了另外一种途径来告诉编译器如何将当前的类型转换成预期类型。
其中最核心的就是自己定义隐式转换方法(implicit conversion function),Scala会根据隐式转换方法的签名,在程序中使用到隐式转换方法接收的参数类型定义的对象时,会自动将其传入隐式转换方法,转换为另外一种类型的对象并返回。所有的隐式值和隐式方法都必须放到object中

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

隐式转换的限制:
1.implicit关键字只能用来修饰方法、变量(参数)
2.隐式转换的方法在当前范围内才有效,如果隐式转换不在当前范围内定义(比如定义在了另一个类中或者包含在某个对象中),就必须通过import语句导入

隐式转换使用方式:
1.将方法或变量标记为implicit
2.将方法的参数列表标记为implicit
3.将类标记为implicit


隐式值:给方法提供参数

//先定义一个普通的方法
scala> def m1(name:String) = println(name)
m1: (name: String)Unit

//给定匹配的参数类型不会报错
scala> m1("LeiJun")
LeiJun
//如果给了不匹配的参数类型就会报错
scala> m1(2)
<console>:13: error: type mismatch;
 found   : Int(2)
 required: String
       m1(2)
          ^
//或者不给参数也会报错
scala> m1
<console>:13: error: missing argument list for method m1
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `m1 _` or `m1(_)` instead of `m1`.
       m1
       ^
//现在重新定义一个方法,参数用implicit修饰
scala> def m2(implicit name:String) = println(name)
m2: (implicit name: String)Unit
//只输出m2依然会报错,不过和之前已经不一样了
//显示的是“找不到参数name: String的隐式值”
scala> m2
<console>:13: error: could not find implicit value for parameter name: String	
       m2
       ^
//那就定义隐式值
scala> implicit val a:String = "money"
a: String = money
//只输出m2发现发生了隐式转换
scala> m2
money

scala> m2("123")
123
//如果再定义一个同类型的隐式值
scala> implicit val b:String = "love"
b: String = love
//就会报错,“爱情面包不可兼得”
scala> m2
<console>:15: error: ambiguous implicit values:
 both value a of type => String
 and value b of type => String
 match expected type String
       m2
       ^

隐式视图

将Int和Double类型转换为String

scala> def m1(name:String) = println(name)
m1: (name: String)Unit
//定义一个将Int类型转换为String的隐式转换方法
scala> implicit def Int2String(x:Int) = x.toString 
warning: there was one feature warning; re-run with -feature for details
Int2String: (x: Int)String

scala> m1(123)
123
//定义一个将Double类型转换为String的隐式转换方法
scala> implicit def Double2String(x:Double) = x.toString
warning: there was one feature warning; re-run with -feature for details
Double2String: (x: Double)String

scala> m1(3.14)
3.14

狗狗学技能(使用别的类中的方法)

狗狗学习导盲技能

class Learn {
  def learnMore(skills:String) = println(skills)
}

// implicit 隐式转换
object Teaching{
  //定义隐式转换的方法 让一个类可以调用另一个类
  implicit def dogLearning(dog: Dog) = new Learn
}

class Dog{
}

object FinalDog{
  def main(args: Array[String]): Unit = {
    //创建Dog对象
    val dog = new Dog
    //手动导入隐式转换方法
    import cn.itcast.scala.implicit_demo.Teaching.dogLearning
    //Dog自己的类中并没有learnMore方法,通过隐式转换就可以直接调用Learn类中的方法
    dog.learnMore("导盲")
  }
}

使用规则

1. Scala一般指考虑处于作用域之内的隐式转换,不过可以通过import调用其他类中的方法,比如上面用到的import cn.itcast.scala.implicit_demo.Teaching.dogLearning

2. 无歧义规则。隐式转换必须在不存在其他可插入转换的前提下才能插入。比如:

scala> implicit def String2Int(s:String) = s.toInt
warning: there was one feature warning; re-run with -feature for details
String2Int: (s: String)Int

scala> implicit def String2Double(s:String) = s.toDouble
<console>:18: error: type mismatch;
 found   : s.type (with underlying type String)
 required: ?{def toDouble: ?}
Note that implicit conversions are not applicable because they are ambiguous:
 both method augmentString in object Predef of type (x: String)scala.collection.immutable.StringOps
 and method String2Int of type (s: String)Int
 are possible conversion functions from s.type to ?{def toDouble: ?}
       implicit def String2Double(s:String) = s.toDouble
                                              ^
<console>:18: error: value toDouble is not a member of String
 Note: implicit method String2Double is not applicable here because it comes after the application point and it lacks an explicit result type
       implicit def String2Double(s:String) = s.toDouble
                                                ^

上面两个都想通过隐式转换同时将String类型转换为Int和Double,如果要进行转换运算时,就会产生歧义,scala会不明白到底是要将输入的String类型参数转换为Int类型还是Double类型

3. 单一调用原则。scala只会尝试一个隐式操作,不会对某个变量执行多次隐式转换。

比如说,现在想将输入的String类型参数转换为Int,然后定义了一个将String类型隐式转换为Int的方法implicit def String2Int(s:String) = s.toInt,结果又想将输入的Int参数转换为Double类型,又将Double类型转换为String类型,编译器将只会尝试最初定义的隐式转换方法

4. 显示操作现行原则。编译器发现代码类型并没有发生错误时,不会进行隐式转换操作。

5.最好将自己定义的隐式转换方法放到一个类中,使用时通过import,这样方便管理,复用性也比较强

原文地址:https://www.cnblogs.com/zzzsw0412/p/12772397.html