我正在设计一个类层次结构,它由一个基类和几个特征组成。基类提供了几种方法的默认实现,并且traits通过abstract override
选择性地覆盖某些方法,以便充当可堆叠的traits / mixins。
从设计的角度来看,这很有效,并且映射到域,这样我就可以从这里添加一个过滤函数(一个特征)和一个来自这里的谓词(另一个特征)等。
但是,现在我想要一些隐藏参数的特性。我很高兴从设计的角度来看这仍然有意义,并且在实践中不会让人感到困惑。但是,我无法说服编译器运行它。
问题的核心似乎是我无法为特征提供构造函数参数,因此可以将它们标记为隐式。在方法实现中引用隐式参数无法使用预期的“无法找到隐式值”消息进行编译;我试图通过
在构造阶段(实际上,它始终在范围内)“隐藏”隐含的方法。implicit val e = implicitly[ClassName]
但是(毫无疑问,你们很多人都希望) 定义失败并带有相同的消息。
这里的问题似乎是我无法说服编译器使用implicit ClassName
标记来标记特征本身的签名,并强制调用者(即将特征混合到对象中的那些人)提供隐含的。目前我的调用者正在这样做,但编译器没有在此级别进行检查。
有没有办法将特征标记为需要在施工时提供某些暗示?
(如果没有,这还没有实现,还是有更深层次的理由说明这是不切实际的?)
答案 0 :(得分:16)
实际上,我之前经常想要这个,但只想出了这个想法。你可以翻译
trait T(implicit impl: ClassName) {
def foo = ... // using impl here
}
到[EDITED:原始版本没有为其他方法提供隐式访问]
trait T {
// no need to ever use it outside T
protected case class ClassNameW(implicit val wrapped: ClassName)
// normally defined by caller as val implWrap = ClassNameW
protected val implWrap: ClassNameW
// will have to repeat this when you extend T and need access to the implicit
import implWrap.wrapped
def foo = ... // using wrapped here
}
答案 1 :(得分:11)
我遇到过这个问题几次,确实有点烦人,但不是太多。摘要成员和参数通常是做同样事情的两种替代方式,各有利弊;对于具有抽象成员的特征并不太不方便,因为无论如何你还需要另一个类来实现这个特征。*
因此,您应该在特征中简单地使用抽象值声明,以便实现类必须为您提供隐式。请参阅以下示例 - 它正确编译,并显示了实现给定特征的两种方法:
trait Base[T] {
val numT: Ordering[T]
}
/* Here we use a context bound, thus cannot specify the name of the implicit
* and must define the field explicitly.
*/
class Der1[T: Ordering] extends Base[T] {
val numT = implicitly[Ordering[T]]
//Type inference cannot figure out the type parameter of implicitly in the previous line
}
/* Here we specify an implicit parameter, but add val, so that it automatically
* implements the abstract value of the superclass.
*/
class Der2[T](implicit val numT: Ordering[T]) extends Base[T]
我展示的基本想法也出现在Knut Arne Vedaa的回答中,但我试图制作一个更引人注目且更方便的例子,不再使用不需要的功能。
*这不是特性不能接受参数的原因 - 我不知道。我只是在争论在这种情况下这种限制是可以接受的。
答案 2 :(得分:11)
这是不可能的。
但您可以使用implicitly
和Scala的类型推断来尽可能轻松地进行此操作。
trait MyTrait {
protected[this] implicit def e: ClassName
}
然后
class MyClass extends MyTrait {
protected[this] val e = implicitly // or def
}
简洁,甚至不需要在扩展类中编写类型。
答案 3 :(得分:0)
你可以这样做:
abstract class C
trait A { this: C =>
val i: Int
}
implicit val n = 3
val a = new C with A {
val i = implicitly[Int]
}
但是我不确定它是否有任何意义 - 你也可以明确地引用隐含值。
我想你想要的是在实例化中摆脱i
的实现,但正如你自己所说,问题的核心是traits不接受构造函数参数 - 无论它们是否会隐含与否并不重要。
此问题的可能解决方案是为已有效的语法添加新功能:
trait A {
implicit val i: Int
}
如果隐式在范围内,编译器将实现i
。
答案 4 :(得分:0)
因为看起来这是不可能的,所以我选择在基类的构造函数上声明隐式val
。正如问题所指出的那样,这并不理想,但它满足了编译器,并且在实际情况下,在我的特定情况下并没有太大的负担。
如果有人有更好的解决方案,我会很高兴听到并接受它。