我正在尝试使用新的apply方法扩展String
,该方法允许我在其上应用更高阶的函数。例如:
case class A(s:String, f: List[String] => List[String])
val f: List[String] => List[String] = { ... stuff ... }
"foo"{f} // == A("foo", f)
所以我已经定义了一个从String到一个带有List[String] => List[String]
函数的apply方法的隐式转换。
implicit def c(s:String) = new {
def apply(f: List[String] => List[String]) = A(s, f)
}
但是当我尝试使用它时,转换会与Predef中的转换相冲突,将String
转换为StringOps
。
scala> "foo"{f}
<console>:19: error: type mismatch;
found : java.lang.String
required: ?{val apply: ?}
Note that implicit conversions are not applicable because they are ambiguous:
both method c in object $iw of type (s: String)java.lang.Object{def apply(f: (List[String]) => List[String]): A}
and method augmentString in object Predef of type (x: String)scala.collection.immutable.StringOps
are possible conversion functions from java.lang.String to ?{val apply: ?}
"foo"{f}
^
为什么它会寻找一个通用的apply方法(required: ?{val apply: ?}
),而不是一个接受我的类型参数(List[String] => List[String]
)的方法?
编辑:
我通过避免使用裸字符串来表达变量来解决这个问题(在我正在研究github的项目中)。所以现在它看起来像这样:
case class param(val v: String) {
def apply(f: Emit.Selector) = Variable(v, f)
}
val $foo = param("foo")
foo{selector} // works fine
我不需要使用暗示。
scala确实在搜索时在implicits的结果类型中查找类型参数。我得到这个工作,但使用函数参数和apply方法的方案不起作用。怎么样?
scala> class A()
defined class A
scala> class B()
defined class B
scala> implicit def one(s:String) = new {
| def a(a:A) = s + " A"
| }
one: (s: String)java.lang.Object{def a(a: A): java.lang.String}
scala> implicit def another(s:String) = new {
| def a(b:B) = s + " B"
| }
another: (s: String)java.lang.Object{def a(b: B): java.lang.String}
scala> "hello" a new A
res1: java.lang.String = hello A
scala> "hello" a new B
res2: java.lang.String = hello B
答案 0 :(得分:2)
当你这样写:
"foo"{f}
编译器会将其翻译为:
"foo".apply { f }
更一般地说:apply
和update
是编译器中存在语法糖的两种特殊方法:
obj(arg) // gets translated to: obj.apply(arg)
obj(index) = arg // gets translated to: obj.update(index, arg)
StringOps已经提供了apply(index: Int)
,并且在寻找隐式转换时,编译器会查找最后提供名为apply
的成员的第一个(无论参数如何)。在你的情况下,你有冲突。
也许您可以将方法重命名为
"foo" useFor { f }
顺便说一下:总是声明隐式转换的返回类型是一种好习惯。此外,您需要在性能关键的情况下避免new { def apply /* ... */ }
样式,因为随后对apply
的任何调用都会通过Java反射进行,这会使其效率低下。
答案 1 :(得分:1)
您可以通过将-Yno-imports
传递给scalac来“禁用”所有标准导入(以及所有标准隐含)(不适用于repl。)。这样可以避免冲突,但是您必须明确导入您使用的所有内容。
答案 2 :(得分:1)
您可以将转换从c更改为augmentString以便遮蔽它,但之后只会找到另一个较低优先级的转换(wrapString)。但是,如果您同时隐藏augmentString并将其添加到LowPriorityImplicits的扩展名中:
object HighPriorityImplicits extends LowPriorityImplicits {
implicit def augmentString(s:String) = new {
def apply(f: List[String] => List[String]) = A(s, f)
}
}
然后它应该工作:
import HighPriorityImplicits._
"foo"{f}
另请参阅:Is there a way to control which implicit conversion will be the default used?