Scala:使用注入定义它的类的类可以访问隐式类

时间:2018-02-28 13:52:44

标签: scala implicit-class

我有一个在其他类中注入的类中定义的隐式类,如

class A {
  implicit class B(s: String) {
    def b = ???
  }
}

class C(a: A) {}

有没有办法从C类访问隐式类B(特别是它的方法b),而无需显式导入它? (请注意,A类不能成为特征,因为它也注入了一些类。)

2 个答案:

答案 0 :(得分:2)

解决方案1 ​​(导入a._

嗯,是的,正如评论中已经提到的,根据您的要求,为什么您不能在a._的正文中导入C并不明显:

class A {
  implicit class B(arg: String) {
    def b: String = ???
  }
}

class C(a: A) { 
  import a._
  { 
    println("hello".b)
  }
}

这一行真的不会伤害任何人。

如果您仍然不喜欢它,那么问题可能出在其他地方。这是我的第二个提议。

解决方案2 (将类似类词类的A - 界面与.b分开 - 语法)

另一个解决方案不是减少代码中import的数量,而且它甚至不会将B保留在A内。但它可能会解决另一个问题,您可能只是非常清楚:它将A提供的功能与B提供的语法分开。

以下代码段的结构受Scala Cats库的设计启发,该库遵循一个非常明确的隐式声明策略,始终将类型类定义与语法分开。

主要思想是:

  • AIntf的实现提供实际功能
  • B仅提供了一些额外的" pimp-my-library" -style方法

我们希望将这两件事分开。

以下是如何将它们分开,从而避免在import a._内部C。首先,定义描述A提供的功能的界面:

  trait AIntf {
    def usefulOperationOnStrings(s: String): String
  }

然后你可以用几个不同的A来实现它:

  class A extends AIntf {
    def usefulOperationOnStrings(s: String): String = "<foo>" + s + "</foo>"
  }

  class A2 extends AIntf {
    def usefulOperationOnStrings(s: String): String = s.toUpperCase
  }

请注意,对象B已从A中消失。相反,它会在单独的syntax - 包中移动,并重命名为A_Ops。方法b也重命名为a

  object syntax /* should be package, object only for script */ {
    object a {
      class A_Ops(wrapped: String, ai: AIntf) {
        def a: String = ai.usefulOperationOnStrings(wrapped)
      }
      implicit def everyStringHasAOps(s: String)(implicit ai: AIntf): A_Ops = {
        new A_Ops(s, ai)
      }
    }
  }

这是您使用它的方式:

  • 您要在导入中说明要引用接口A_Intf
  • 您在导入中说要使用语法syntax.a._
  • 您将a - C的参数声明为隐式
  • 然后您可以在"string".a内使用C语法而无需进一步导入。

在代码中:

import myproject.AIntf
import myproject.syntax.a._

class C(implicit val a: AIntf) {
  {
    println("hello".a)
  }
}

现在AIntf和语法.a的实现变得独立。您可以注入A2而不是A。或者,您可以将语法从"str".a更改为"str".somethingEntirelyDifferent

完整的代码段:

import scala.language.implicitConversions

object myproject /* should be package, object only for script */ {

  trait AIntf {
    def usefulOperationOnStrings(s: String): String
  }

  object syntax /* should be package, object only for script */ {
    object a {
      class A_Ops(wrapped: String, ai: AIntf) {
        def a: String = ai.usefulOperationOnStrings(wrapped)
      }
      implicit def everyStringHasAOps(s: String)(implicit ai: AIntf): A_Ops = {
        new A_Ops(s, ai)
      }
    }
  }

  class A extends AIntf {
    def usefulOperationOnStrings(s: String): String = "<foo>" + s + "</foo>"
  }

  class A2 extends AIntf {
    def usefulOperationOnStrings(s: String): String = s.toUpperCase
  }
}



import myproject.AIntf
import myproject.syntax.a._

class C(implicit val a: AIntf) {
  {
    println("hello".a)
  }
}

val c1 = new C()(new myproject.A)
val c2 = new C()(new myproject.A2)

// prints:
// <foo>hello</foo>
// HELLO

不幸的是,我不清楚guice将如何处理隐式参数,还没有尝试过。它可能会迫使你写

class C @Inject()(val a: AIntf) {
  implicit aintf: AIntf = a
  ...
}

然后变得比第一部分中提到的简单import长。

答案 1 :(得分:0)

正如评论中所述,只是import a._

class A {
  implicit class B(s: String) {
    def b: String = "hello "+ s
  }
}
class C(a: A){
  import a._
  val hello = "world".b
}

val c = new C(new A)
c.hello // "hello world"