Scala:使隐式 class 可从 classes 访问,注入定义它的 class
Scala: make an implicit class accessible from classes injecting the class defining it
我在 class 中定义了一个隐式 class 注入到其他 class 中,像这样
class A {
implicit class B(s: String) {
def b = ???
}
}
class C(a: A) {}
有没有一种方法可以从 class C 访问隐式 class B(特别是它的方法 b),而无需显式导入它? (请注意 class A 不能是特征,因为它也注入了一些 classes。)
如评论中所述,只是 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"
解决方案 1(导入 a._
)
嗯,是的,正如评论中已经指出的那样,根据您的要求,您为什么不在 C
的正文中导入 a._
并不明显:
class A {
implicit class B(arg: String) {
def b: String = ???
}
}
class C(a: A) {
import a._
{
println("hello".b)
}
}
这一行真的伤不了人。
如果您仍然不喜欢它,那么问题可能出在其他地方。因此我的第二个提议。
解决方案 2(将 typeclass-like A
-接口与 .b
-语法分开)
这个其他解决方案与减少代码中 import
的数量无关,它甚至没有将 class B
保留在 A
中。但它可能会解决另一个您可能不太清楚的问题:它将 A
提供的功能与 B
.
提供的语法分开
以下代码段的结构受到 Scala Cats 库设计的启发,该库遵循非常明确的隐式声明策略,始终将类型 class 定义与语法分开。
主要思想是:
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
包中,并重命名为 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._
- 您将
C
的 a
参数声明为隐式
- 然后你可以在
C
中使用 "string".a
语法,无需进一步导入。
在代码中:
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
更长。
我在 class 中定义了一个隐式 class 注入到其他 class 中,像这样
class A {
implicit class B(s: String) {
def b = ???
}
}
class C(a: A) {}
有没有一种方法可以从 class C 访问隐式 class B(特别是它的方法 b),而无需显式导入它? (请注意 class A 不能是特征,因为它也注入了一些 classes。)
如评论中所述,只是 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"
解决方案 1(导入 a._
)
嗯,是的,正如评论中已经指出的那样,根据您的要求,您为什么不在 C
的正文中导入 a._
并不明显:
class A {
implicit class B(arg: String) {
def b: String = ???
}
}
class C(a: A) {
import a._
{
println("hello".b)
}
}
这一行真的伤不了人。
如果您仍然不喜欢它,那么问题可能出在其他地方。因此我的第二个提议。
解决方案 2(将 typeclass-like A
-接口与 .b
-语法分开)
这个其他解决方案与减少代码中 import
的数量无关,它甚至没有将 class B
保留在 A
中。但它可能会解决另一个您可能不太清楚的问题:它将 A
提供的功能与 B
.
以下代码段的结构受到 Scala Cats 库设计的启发,该库遵循非常明确的隐式声明策略,始终将类型 class 定义与语法分开。
主要思想是:
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
包中,并重命名为 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._
- 您将
C
的a
参数声明为隐式 - 然后你可以在
C
中使用"string".a
语法,无需进一步导入。
在代码中:
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
更长。