2009-08-27 184 views
15

我试图创建任何类型为String的隐式转换(比如,智力)避免隐DEF歧义......在斯卡拉

的隐式转换为字符串表示RichString方法(如反向)不可用。

implicit def intToString(i: Int) = String.valueOf(i) 
100.toCharArray // => Array[Char] = Array(1, 0, 0) 
100.reverse // => error: value reverse is not a member of Int 
100.length // => 3 

的隐式转换到RichString意味着字符串方法(如toCharArray)不可用

implicit def intToRichString(i: Int) = new RichString(String.valueOf(i)) 
100.reverse // => "001" 
100.toCharArray // => error: value toCharArray is not a member of Int 
100.length // => 3 

同时使用隐式转换装置重复方法(如长度)是不明确的。

implicit def intToString(i: Int) = String.valueOf(i) 
implicit def intToRichString(i: Int) = new RichString(String.valueOf(i)) 
100.toCharArray // => Array[Char] = Array(1, 0, 0) 
100.reverse // => "001" 
100.length // => both method intToString in object $iw of type 
    // (Int)java.lang.String and method intToRichString in object 
    // $iw of type (Int)scala.runtime.RichString are possible 
    // conversion functions from Int to ?{val length: ?} 

那么,是否可以隐式转换为字符串,并且仍然支持所有的String和RichString方法?

回答

2

要么使一个巨大的代理类,或吸起来,并要求客户将其消除歧义:

100.asInstanceOf [字符串]。长度

+0

看起来'吮吸'是最好的选择。 Scala中的API设计比成为API消费者要困难得多。 我想创建一个包装值的数据类型,并允许API用户将数据类型当作是实际值的类型。例如。如果它包装一个Int,然后像int一样处理对象。原来这太难了。 我在考虑给我的数据类型的辅助方法: T型 VAL internedValue:T 高清S = internedValue.asInstanceOf [字符串] 高清I = internedValue.asInstanceOf [INT] ...等 – Synesso 2009-08-27 23:38:51

+20

身高'( 100:String).length',因为这是类型安全的,而'asInstanceOf'不是。此外,它更漂亮,更短。 – 2009-12-11 11:04:18

+2

不仅如此,而且asInstanceOf在这里永远不会工作,因为它只会向下转换(这在这里肯定会失败),并且不会启动任何隐式转换。换句话说,'100.asInstanceOf [String] .length'将总是失败,出现'ClassCastException' – 2015-05-06 08:01:37

2

我看到的唯一选择是创建一个新的字符串包装类MyString的,让你想要的任何方法调用的暧昧情况下被调用。然后你可以定义到MyString的隐式转换和从MyString到String和RichString的两个隐式转换,以防需要将它传递给库函数。

+0

这听起来像一个公平的方法,但很多工作。另外我怀疑这不是未来的证明,因为引入了其他隐式转换。 – Synesso 2009-08-27 23:43:11

+0

确定这是很多工作,但不是很多思考,只是打字。 ;)为什么它不是未来的证明?没有人强迫您使用未来可能推出的任何隐式转换,甚至不包括预加载中的转换。 – 2009-08-28 19:32:00

+0

@Kim Stebel:'当然这是很多工作,但不是很多思考,只是打字'程序员是懒惰的思想家,而不是勤劳的打字员。 – 2012-08-22 08:28:38

1

我很困惑:你不能用在任何类型的.toString反正从而避免了隐式转换的需要?

+0

我不认为这会改变事情,除了可能引入空指针异常。 要清楚,您正在建议将String.valueOf(i)更改为i.toString? – Synesso 2009-08-27 23:41:18

5

我没有一个解决方案,但会发表评论其原因RichString方法不是后您的intToString隐含可用的是,Scala没有链隐含调用(请参阅21.2“规则implicits”在编程在斯卡拉)。

如果引入中间String,则Scala会将该隐含对话转换为RichString(该隐含定义在Predef.scala中)。

例如,

$ scala 
Welcome to Scala version 2.7.5.final [...]. 
Type in expressions to have them evaluated. 
Type :help for more information. 

scala> implicit def intToString(i: Int) = String.valueOf(i) 
intToString: (Int)java.lang.String 

scala> val i = 100 
i: Int = 100 

scala> val s: String = i 
s: String = 100 

scala> s.reverse 
res1: scala.runtime.RichString = 001 
+0

这很好理解!谢谢。 – Synesso 2009-08-27 23:39:30

+0

这是“唯一”的允许循环隐式可转换关系的缺点,还是有其他原因?因为即使你有一个圆圈,你也应该静态地找到“最近”的实现。在没有访问图表的情况下浏览图表并不困难,尽管可能需要一段时间。 – Raphael 2011-01-18 15:31:43

5

作为Scala的2.8的,这已被提高。按照this paper(§避免歧义):

以前,最特殊的重载方法或隐式转换 将仅基于该方法的参数类型来选择。有其说,最特殊的方法不能在任何其他替代品的正确定义超德 的科幻 附加条款。这个 方案已被Scala 2.8替换为更宽泛的方法: 当比较重载方法或隐式方法的两种不同适用方案时,每种方法都会获得一个点以获得更多的 特定参数,另一个要点被定义在适当的 子类中。另一种“赢”在另一如果它得到点在这两个比较有较大数量的 。这尤其意味着如果 替代方案具有相同的参数类型,则在 子类中定义的参数将获胜。

查看that other paper(第6.5节)举例。

+0

您的第一张纸的链接已损坏。 – 2014-03-26 03:00:33

2

接受的解决方案(由Mitch Blevins发布)将永远不会工作:向下铸造IntString使用asInstanceOf将始终失败。

一个解决问题的方法是从任何字符串转换类型新增转换到RichString(或者更确切地说,以StringOps因为它现在被命名):

implicit def stringLikeToRichString[T](x: T)(implicit conv: T => String) = new collection.immutable.StringOps(conv(x)) 

然后定义您的转换(或多个)字符串如前所示:

scala> implicit def intToString(i: Int) = String.valueOf(i) 
warning: there was one feature warning; re-run with -feature for details 
intToString: (i: Int)String 

scala> 100.toCharArray 
res0: Array[Char] = Array(1, 0, 0) 

scala> 100.reverse 
res1: String = 001 

scala> 100.length 
res2: Int = 3