0

我在注入其他类的类中定义了一个隐式类

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

class C(a: A) {}

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

4

2 回答 2

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(将 typeclass-like -interfaceA.b-syntax 分开)

这个另一种解决方案不是减少import代码中 s 的数量,它甚至不保留 class Binside A。但它可能会解决另一个您可能无法清楚表达的问题:它将 提供的功能与提供A的语法分开B

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

主要思想是:

  • AIntf提供实际功能的实现
  • B仅提供一些额外的“pimp-my-library”风格的方法

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

这是如何将它们分开的方法,从而也避免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-package 中,并重命名为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._
  • 您将 - 参数声明aC隐式
  • 然后你可以在"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比第一部分中提到的简单。

于 2018-02-28T15:12:28.283 回答
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"
于 2018-02-28T14:47:38.340 回答