学过java的获取其他有基本类型语言的都应该知道隐式转换。。。像byte可以默认转为short、int、long。
而scala没有基本类型,只有对象,所以肯定不是像上面那样直接转换的。
所以,就有了隐式值、隐式方法、隐式类这三种实现隐式转换的特殊语法(很不喜欢语法糖太多的语言,因为我记忆力不好,初学要记得东西太多,理解了也没用,过段时间不也还会忘,毕竟自己私下学,没有用在项目中,所以不经常用)
关于隐式转换都涉及到一个关键字 implicit,知道一下就行。
一、隐式值
首先,隐式值是用在被调用的方法上的,对于被调用的方法有一定的限制。即:被使用的方法必须只有一个参数,且该参数也需要在前面声明implicit。
使用方式:
(1)在被调用方法调用之前先使用implicit定义一个变量。
(2)使用不带括号的方式调用方法(即直接写方法名),此时调用该方法时,就会把步骤一中的变量值作为参数传入。如果调用方法时加了括号(不论加没加参数),那么就不会使用隐式值
(3)如果使用不带括号的方式调用该方法,且符合(1)的条件,那么会先使用前面定义的隐式变量。如果在调用该方法之前没有定义隐式变量,那么就会使用该方法参数所定义的默认值,如果该参数没有定义默认值,则报错(那就需要主动传入参数才行)。。
具体使用例子如下:
object Test {
def main(args: Array[String]): Unit = {
//需要在调用User.test方法之前定义该隐式值变量
implicit val name: String = "我是隐式变量" //第4行
User.test
}
}
object User {
//参数加一个implicit关键字定义该参数可以使用隐式值
def test(implicit name: String = "我是默认值"): Unit = {
println("打印参数:" + name)
}
}
输出结果如下:
打印参数:我是隐式变量
如果把第4行注释掉,则打印结果变为:
打印参数:我是默认值
但implicit放在方法参数位置还有几个需要注意的:
(1)只能放在方法的参数列表开始位置,并且同一个方法只能写一次implicit关键字。用以表名该方法的参数都会优先从作用域范围查找并使用隐式值。像下面这样就不符合scala语法了。
def test(implicit name: String, implicitint: String): Unit
def test( name: String, implicitint: String): Unit
(2)implicit写在参数列表开始位置之后,表名该方法的所有参数都优先使用隐式值(如果有的话),而不仅仅是第一个参数。
(3)implicit所写的方法参数(如果是柯里化函数,有多个括号的,那么只对implicit所写的那个括号内的参数起作用),然后在调用该函数的时候,这个括号必须省略不写,否则就不是使用隐式值,而是需要传参或者使用参数默认值了。
(4)在隐式值与默认值同时作用与一个参数时,当符合使用隐式值的时候,优先使用隐式值,只有在隐式值无法使用时,才会退而求其次使用参数默认值。
二、隐式方法
使用隐式方法的好处就是可以直接通过隐式转换前的对象调用转换后的类型方法。可能绕口,那请看下面示例代码:
object Test {
def main(args: Array[String]): Unit = {
var t = new TestInsert()
implicit def transformDelete(test1: TestInsert): TestDelete = {
new TestDelete
}
implicit def transformUpdate(test1: TestInsert): TestUpdate = {
new TestUpdate
}
//这行代码编译时不会作替换,insert本来就是t对象所属类型的方法
t.insert()
//编译时:由于t调用了delete方法,不属于t所属类型的方法,
//然后就会找匹配t的类型的隐式方法,这里能够找到两个transformDelete和transformUpdate,这两个隐式方法的参数都是TestInsert
//然后在transformDelete方法返回的类型中找到了delete方法,
//所以编译的时候把t替换为transformDelete(t),所以编译后是变为:transformDelete(t).delete()
t.delete()
//与上面同理,只不过也是在transformUpdate返回类型中找到update,所以是把t替换为transformUpdate(t).update()
t.update()
//这个就不用讲了吧,new TestInsert()这个对象也是能匹配到上面的隐式方法,然后...就那样
new TestInsert().delete()
}
}
class TestInsert {
def insert(): Unit = {
println("TestInsert")
}
}
class TestDelete {
def delete(): Unit = {
println("TestDelete")
}
}
class TestUpdate {
def update(): Unit = {
println("TestUpdate")
}
}
输出结果是:
TestInsert
TestDelete
TestUpdate
TestDelete
反正,底层还是去调用方法转换,性能上没有任何优化的。。
使用限制:
(1)隐式方法只能有一个参数
(2)如果在在使用时的作用域能访问到多个隐式方法,匹配相同类型的隐式方法的返回值不能是同一个类型,不仅仅类型必须不同,返回的类型中也尽量不能有相同签名的方法。如果相同签名的方法没被调用还好,如果该方法被调用了,那么就会出错。
比如上面的TestUpdate类改为
class TestUpdate {
def update(): Unit = {
println("TestUpdate")
}
def delete(): Unit = {
println("TestDelete")
}
}
那就不能执行了,因为TestDelete和TestUpdate都有delete方法,执行t.delete根本不知道把隐式转换为TestDelete和TestUpdate中的哪一个类型。
三、隐式类
其实和隐式方法基本没差别。
和隐式方法对比着说更容易理解:
(1)隐式类只能有一个构造方法,并且该构造方法的参数必须为一个.................这和隐式方法的参数一样。
(2)隐式类本身作为转换后的类型...................................................................相当于隐式类型本身的实例作为隐式方法的返回值一样。
(3)隐式类定义的位置不能在文件的顶级代码块中,必须放在伴生对象、类或者包对象代码块内才行。(隐式类特有限制)
其实就类似于java的包装类,搞个语法糖而已,又浪费了我三个小时写这博客,好烦。