Scala隐式转换的问题分析-String隐式转换为Int

引出问题

首先来看一个需求:将String类型的数字赋值给Int类型的变量。

也就是这样:

val num:Int="20"

要想实现这样的效果,小伙伴们应该都能想到使用隐式方法这个技能。许多小伙伴一鸡冻就撸出了如下的代码:

implicit def strToInt(str:String):Int= {str.toInt}

***友情提示:***隐式转换的代码要定义在object中哦~~~

定义了如上的隐式方法后,接下来我们来使用一下该隐式方法。接下来来一段完整代码尝尝:

object TestDemo {def main(args: Array[String]): Unit = {val num:Int = "20"println(num)}/*** 定义的隐式方法*    该方法的功能是将String转成Int* @param str  需要转换的字符串* @return 返回 Int*/implicit def strToInt(str:String):Int= {str.toInt}}

代码撸完,感觉还不错,接着我们运行以上代码:哐当,出错啦。。。。

控制台输出以下错误信息:

Error:(17, 5) type mismatch;found   : str.type (with underlying type String)required: ?{def toInt: ?}
Note that implicit conversions are not applicable because they are ambiguous:both method augmentString in object Predef of type (x: String)scala.collection.immutable.StringOpsand method strToInt in object TestDemo of type (str: String)Intare possible conversion functions from str.type to ?{def toInt: ?}str.toInt
Error:(17, 9) value toInt is not a member of Stringstr.toInt

大伙看错误信息中有个关键的单词ambiguous(模糊不清的,模棱两可的),以上错误信息的大致意思是说:

隐式转换在这里不适用了,因为隐式转换出现了模糊不清的情况,这里有两个方法
一个在object Predef中有一个augmentString方法将x:String转成scala.collection.immutable.StringOps,
另一个在object TestDemo中有strToInt方法将str: String转成了Int
以上两个方法都可以将str: String转成了Int,所以隐式转换出现了模糊不清的情况

看完后估计有的小伙伴还是一头雾水,这到底是说的啥,这到底是为什么??好,那接下来我们就看看其中的究竟。

分析原因

分析String相关的源码

首先我们来看一段代码:

 def main(args: Array[String]): Unit = {val str:String = "20"val i = str.toIntprintln(i)}

以上代码的正确编译运行,输出结果是Int类型的20

接下来我们按住键盘上的Ctrl,鼠标点击String查看String类型,发现在object Predef中有这么一段代码:

type String        = java.lang.String

通过这段代码我们知道,Scala中的String其实是使用了java中的String。好了,那我们回顾以下java中的String有toInt()方法吗,熟悉Java的肯定立马就能回答:没有。对的,就是没有!!

String中没有toInt()方法,但是这里的str却可以使用toInt(),那说明str.toInt这里发生了隐式转换。没错,这里的str由String转换成了StringOps。这么转换的呢,我们上源码:

implicit def augmentString(x: String): StringOps = new StringOps(x)

在object Perdef中有一个隐式方法augmentString将String转换成了StringOps。接着我们再来看看StringOps的源码:

final class StringOps(override val repr: String) extends AnyVal with StringLike[String] {override protected[this] def thisCollection: WrappedString = new WrappedString(repr)override protected[this] def toCollection(repr: String): WrappedString = new WrappedString(repr)/** Creates a string builder buffer as builder for this class */override protected[this] def newBuilder = StringBuilder.newBuilderoverride def apply(index: Int): Char = repr charAt indexoverride def slice(from: Int, until: Int): String = {val start = if (from < 0) 0 else fromif (until <= start || start >= repr.length)return ""val end = if (until > length) length else untilrepr.substring(start, end)}override def toString = reproverride def length = repr.lengthdef seq = new WrappedString(repr)
}

在StringOps中没有直接发现toInt()方法,不要慌,仔细看看StringOps继承了StringLike,接着我们看看StringLike源码:

trait StringLike[+Repr] extends Any with scala.collection.IndexedSeqOptimized[Char, Repr] with Ordered[String] {//省略代码................def toInt: Int         = java.lang.Integer.parseInt(toString)/*** @throws java.lang.NumberFormatException  - If the string does not contain a           parsable long.*/def toLong: Long       = java.lang.Long.parseLong(toString)}

终于在StringLike中找到了toInt,因为StringOps继承了StringLike,所以StringOps也就有了toInt()。

再看刚才的隐式方法:

implicit def augmentString(x: String): StringOps = new StringOps(x)

隐式方法augmentString将String转换成了StringOps,所以:

 def main(args: Array[String]): Unit = {val str:String = "20"val i = str.toInt // 底层代码实现:augmentString(str).toIntprintln(i)}

好了,通过以上代码分析,我们知道StringOps中存在toInt方法,所以通过隐式转换将str转成StringOps后就可以调用toInt了。

分析Int相关的源码

接下来,我们继续分析Int类的源码:

final abstract class Int private extends AnyVal {//省略代码def toInt: Intdef toLong: Long//省略代码}

通过查看Int的源码发现在Int中也存在一个toInt的方法,那么现在如果也存在一个将String转成Int的隐式方法,那么,String也能调用toInt了。分析到这里,我们渐渐的感觉到发现冲突产生的地方了,好的,我们马上来看看我们写的隐式方法的代码:

 /*** 定义的隐式方法*    该方法的功能是将String转成Int* @param str  需要转换的字符串* @return 返回 Int*/
implicit def strToInt(str:String):Int= {/*** 通过分析我们知道StringOps和Int都有toInt方法,所以* str.toInt在这里有两个隐式函数都可以进行转换* 1、使用scala.Predef 中的*       implicit def augmentString(x: String): StringOps = new StringOps(x)*   所以str.toInt 就等价于  augmentString(str).toInt** 2、使用自己定义这个隐式方法(递归调用)*      augmentString 就等价于 strToInt(str).toInt**  好了,我们知道隐式转换只能匹配一个,不能有多个,*  而这里str.toInt找到两个隐式转换都可以实现,所以出现了之前“模棱两可”的错误信息*/str.toInt}

既然分析清楚了出现错误的原因,接下来我们就根据原因来看看如何解决吧。

解决方法的本质就是不要让toInt隐式转换的时候找到多个隐式方法出现“模棱两可”。

解决方案

方案一

使用Integer.parseInt(str)替换str.toInt。完整代码代码如下:

object TestDemo {def main(args: Array[String]): Unit = {val str:Int = "20"println(str)}implicit def strToInt(str:String):Int= {Integer.parseInt(str)}}

使用Integer.parseInt(str)替换str.toInt,没有使用str.toInt了,隐式转换自然就就没有了。

方案二

使用new StringOps(str).toInt替换str.toInt。完整代码代码如下:

object TestDemo {def main(args: Array[String]): Unit = {val str:Int = "20"println(str)}implicit def strToInt(str:String):Int= {new StringOps(str).toInt}}

使用new StringOps(str).toInt替换str.toInt。这里使用new StringOps(str)显式调用了toInt,所以也没有隐式转换了。

方案三

将自己定义的隐式方法implicit def strToInt(str:String):Int的返回值省略,该方法就不能被递归调用了,那这时候str.toInt就只有implicit def augmentString(x: String): StringOps这一个隐式转换方法了,也解决了两个隐式方法的冲突问题。完整代码如下:

object TestDemo {def main(args: Array[String]): Unit = {val str:Int = "20"  // errorprintln(str)}implicit def strToInt(str:String) = {str.toInt}}

以上代码第四行val str:Int = "20"编译错误,错误信息如下:

Error:(8, 19) type mismatch;found   : String("20")required: IntNote: implicit method strToInt is not applicable here because it comes after the application point and it lacks an explicit result typeval str:Int = "20"

出现该错误的原因是:

隐式方法没有显式给出返回类型,必须位于应用点之前

所以正确代码如下:

object TestDemo {implicit def strToInt(str:String) = {str.toInt}def main(args: Array[String]): Unit = {val str:Int = "20"println(str)}}

好了,到这里我们的问题分析及解决方案就结束了,希望对大家有所帮助。
by 木子李 更多技术文章

Scala隐式转换的问题分析--String隐式转换为Int相关推荐

  1. sql隐式转换_SQL Server中的隐式转换

    sql隐式转换 This article will provide an overview of SQL Server implicit conversion including data type ...

  2. c# 无法将类型隐式转换_C#中的隐式类型数组

    c# 无法将类型隐式转换 C#隐式类型数组 (C# Implicitly Typed Arrays) Like implicitly typed variables, we can also decl ...

  3. php隐式转换,隐式转换如何使用?总结隐式转换实例用法

    JavaScript的数据类型分为六种,分别为null,undefined,boolean,string,number,object.object是引用类型,其它的五种是基本类型或者是原始类型.我们可 ...

  4. oracle隐式转换能禁用吗,Oracle隐式转换

    和其他的关系型数据库一样, oracle 中也能进行一些隐式的数据转换,这对我们写 SQL 语句有 非常 用,我们可以不必麻烦地手动转化很多类型的字符.虽然前面我们介绍了一些使用例如to_char,t ...

  5. android 尺寸转换 dp sp in mm pt px转换为int

    在android开发中,在自定义view中有些函数只接受int参数,而我们经常使用的尺寸单位是dp.sp这些,那如何将它们转换为int型呢? int spToInt=TypedValue.applyD ...

  6. 2021年大数据常用语言Scala(三十八):scala高级用法 隐式转换和隐式参数

    目录 隐式转换和隐式参数 隐式转换 自动导入隐式转换方法 隐式转换的时机 隐式参数 隐式转换和隐式参数 隐式转换和隐式参数是scala非常有特色的功能,也是Java等其他编程语言没有的功能.我们可以很 ...

  7. scala中的隐式转换、隐式参数和隐式类

    scala中的隐式转换.隐式参数和隐式类 @(SCALA)[scala] scala中的隐式转换隐式参数和隐式类 一隐式转换 1示例 2隐式转换的条件 二隐式参数 1示例 三隐式类 1示例 隐式转换是 ...

  8. Scala入门到精通——第十九节 隐式转换与隐式参数(二)

    本节主要内容 隐式参数中的隐式转换 函数中隐式参数使用概要 隐式转换问题梳理 1. 隐式参数中的隐式转换 前一讲中,我们提到函数中如果存在隐式参数,在使用该函数的时候如果不给定对应的参数,则编译器会自 ...

  9. Scala入门到精通——第十八节 隐式转换与隐式参数(一)

    本节主要内容 隐式转换简介 隐式转换函数 隐式转换规则 隐式参数 1. 隐式转换简介 在Scala语言当中,隐式转换是一项强大的程序语言功能,它不仅能够简化程序设计,也能够使程序具有很强的灵活性.要想 ...

  10. scala学习之旅(十三):隐式转换和隐式参数

    文章地址:http://www.haha174.top/admin/article/list 1.引言 scala 提供的隐式转换和隐式参数功能,是非常有特色的功能.是java 等编程语言所没有的功能 ...

最新文章

  1. [lua]判断nginx收到的是否json
  2. 第十五届全国大学生智能汽车竞赛华南赛区成绩总结
  3. DSP专家给你一个选择FPGA的理由(让人看了热血沸腾的一篇科普文章)
  4. eCos Mbox机制
  5. android layout 层次感,FrameLayout的层次问题
  6. Kontakt 6 for Mac(强大的音频采样器软件)
  7. 大数据Hadoop(五):Hadoop架构
  8. vue实现调用摄像头扫描二维码功能
  9. 工业机器人技术试题_工业机器人考试试题库
  10. cadz轴归零命令_CAD中所有图形实现统一标高(Z轴归零)的方法,统一标高的快捷键命令...
  11. 2013八大免费杀毒软件排行榜
  12. WinCC界面中通过插件打开pdf文件
  13. 中学生心理测试系统软件,学校心理测评软件
  14. 呼叫中心外呼系统与双呼系统对比
  15. Android 版本号和分支查看
  16. DNS中cname记录的作用
  17. [python]pycharm自动生成函数注释
  18. 数字正交下变频(多相滤波法)
  19. 电影数据分析——国产烂片深度揭秘
  20. Android 更换应用图标无效

热门文章

  1. 微信推送封面尺寸_微信公众号推送文图片什么尺寸最佳?
  2. Linux下通过vi修改只读文件
  3. 智能眼镜现在是什么水平?
  4. 网站安全公司对渗透测试行业的运营观点
  5. Quadratic equation
  6. 头像怎么画,日系头像绘画教程
  7. 织梦dedeCMS留言薄
  8. 移动端touch触屏滑动事件、滑动触屏事件监听!
  9. 移动端web开发click touch tap区别
  10. STM32Cube配置LCD1602液晶屏