有没有办法"丰富"Scala类而不将代码包装到另一个对象中?

Iva*_*van 15 scala enrich-my-library

在Scala 2.9中为库类添加自定义方法(丰富或"皮条客")我不得不写这样的东西:

object StringPimper {
  implicit def pimpString(s: String) = new {
    def greet: String = "Hello " + s
  }
}
Run Code Online (Sandbox Code Playgroud)

随着Scala 2.10的发布,我读到它引入了隐式类定义,从理论上讲,它是通过在返回匿名类对象的隐式方法中消除需要来简化上述任务.我以为这会让我写出来

implicit class PimpedString(s: String) {
  def greet: String = "Hello " + s
}
Run Code Online (Sandbox Code Playgroud)

这对我来说看起来更漂亮.但是,这样的定义会导致编译错误:

`implicit' modifier cannot be used for top-level objects
Run Code Online (Sandbox Code Playgroud)

通过将代码再次包装在对象中来解决:

object StringPimper {
  implicit class PimpedString(s: String) {
    def greet: String = "Hello " + s
  }
}
Run Code Online (Sandbox Code Playgroud)

不用说,这几乎抵消了改善的意义.

那么,有没有办法把它写得更短?要摆脱包装器对象?

我实际上有一个MyApp.pimps所有皮条客都去的包(我没有太多,如果我有的话,我会使用一些单独的包)而且我厌倦了导入MyApp.pimps.StringPimper._而不是MyApp.pimps.PimpedStringMyApp.pimps._.当然,我可以将所有隐式类放在一个包装器对象中,但这意味着将它们全部放在一个文件中,这将是相当长的 - 非常难看的解决方案.

Rex*_*err 10

现在标准术语是丰富的.我不确定为什么它之前没有,因为使用它的库方法被命名为RichString不是PimpedString.

无论如何,你不能把隐式类放在什么都没有,但为了使用implicits你需要方法,你不能把方法放在什么都没有.但你可以作弊来获取所有内容pimps.

// This can go in one file
trait ImplicitsStartingWithB {
  implicit class MyBoolean(val bool: Boolean) { def foo = !bool }
  implicit class MyByte(val byte: Byte) { def bar = byte*2 }
}

// This can go in another file
trait ImplicitsStartingWithS {
  implicit class MyShort(val short: Short) { def baz = -short }
  implicit class MyString(val st: String) { def bippy = st.reverse }
}

// This is a third file, if you want!
object AllImplicits extends ImplicitsStartingWithB with ImplicitsStartingWithS {}

scala> import AllImplicits._
import AllImplicits._

scala> true.foo
res0: Boolean = false
Run Code Online (Sandbox Code Playgroud)

您也可以使用pimps包对象执行此操作- 通常您将在名为package.scala的目录中放置一个名为的文件pimps,然后

package object pimps extends /* blah blah ... */ {
  /* more stuff here, if you need it */
}
Run Code Online (Sandbox Code Playgroud)

需要注意的是,作为新功能的价值类具有相当多的限制.有些是不必要的,但是如果你想避免分配一个新的MyBoolean类(JVM通常可以大大优化,但通常没有达到裸方法调用的程度),你将不得不解决这些限制.在这种情况下,

// In some file, doesn't really matter where
class MyBoolean(val bool: Boolean) extends AnyVal { def foo = !bool }

package object pimps {
  def implicit EnrichWithMyBoolean(b: Boolean) = new MyBoolean(b)
}
Run Code Online (Sandbox Code Playgroud)

这不会节省你的工作(但运行得更快).


0__*_*0__ 5

允许扩充类驻留在不同文件中的解决方法是将其包装在特征中而不是对象中:

// file: package.scala
package object mypackage extends StringImplicits {
  def test { println("Scala".greet) }
}

// file: StringImplicits.scala
package mypackage

trait StringImplicits {
  implicit class RichString(s: String) {
    def greet: String = "Hello " + s
  }
}
Run Code Online (Sandbox Code Playgroud)

正如Rex指出的那样,您不能在其他类中使用值类。因此,如果您想从Scala 2.10的值类中受益,则不能使用嵌套的隐式类。天哪!


Leo*_*Leo 3

好吧,编译器错误说明了一切。您只是无法在顶层定义隐式类。这意味着您要么使用包装对象(也可以是包对象),要么直接在要使用的范围中定义该类(在极少数情况下,它不必可重用)。

原因是如果没有包装器,您将无法导入隐式转换。您不能通过其自己的名称导入隐式类(准确地说:转换)。

除此之外,我建议使用稍微不同的签名(请参阅值类):

implicit class PimpedString(val self: String) extends AnyVal
Run Code Online (Sandbox Code Playgroud)

其效果是对扩展方法的调用可以(并且将会)内联。